C/C++/C#面試題精選(1)
題目(一):C++中我們可以用static修飾一個類的成員函數,也可以用const修飾類的成員函數(寫在函數的最后表示不能修改成員變量,不是指寫在前面表示返回值為常量)。請問:能不能同時用static和const修飾類的成員函數?
分析:答案是不可以。C++編譯器在實現const的成員函數的時候為了確保該函數不能修改類的實例的狀態,會在函數中添加一個隱式的參數const this*。但當一個成員為static的時候,該函數是沒有this指針的。也就是說此時static的用法和static是沖突的。
我們也可以這樣理解:兩者的語意是矛盾的。static的作用是表示該函數只作用在類型的靜態變量上,與類的實例沒有關系;而const的作用是確保函數不能修改類的實例的狀態,與類型的靜態變量沒有關系。因此不能同時用它們。
題目(二):運行下面C++代碼,輸出是什么?
class A
{
};
?
class B
{
public:
???????B() {}
???????~B() {}
};
?
class C
{
public:
???????C() {}
???????virtual ~C() {}
};
?
int _tmain(int argc, _TCHAR* argv[])
{
???????printf("%d, %d, %d/n",sizeof(A),sizeof(B),sizeof(C));
???????return 0;
}
分析:答案是1, 1, 4。class A是一個空類型,它的實例不包含任何信息,本來求sizeof應該是0。但當我們聲明該類型的實例的時候,它必須在內存中占有一定的空間,否則無法使用這些實例。至于占用多少內存,由編譯器決定。Visual Studio 2008中每個空類型的實例占用一個byte的空間。
class B在class A的基礎上添加了構造函數和析構函數。由于構造函數和析構函數的調用與類型的實例無關(調用它們只需要知道函數地址即可),在它的實例中不需要增加任何信息。所以sizeof(B)和sizeof(A)一樣,在Visual Studio 2008中都是1。
class C在class B的基礎上把析構函數標注為虛擬函數。C++的編譯器一旦發現一個類型中有虛擬函數,就會為該類型生成虛函數表,并在該類型的每一個實例中添加一個指向虛函數表的指針。在32位的機器上,一個指針占4個字節的空間,因此sizeof(C)是4。
題目(三):運行下面的C++代碼,得到的結果是什么?
class A
{
private:
???????int m_value;
?
public:
???????A(int value)
???????{
???????????????m_value = value;
???????}
???????void Print1()
???????{
???????????????printf("hello world");
???????}
???????void Print2()
???????{
???????????????printf("%d", m_value);
???????}
};
?
int _tmain(int argc, _TCHAR* argv[])
{
???????A* pA = NULL;
???????pA->Print1();
???????pA->Print2();
?
???????return 0;
}
分析:答案是Print1調用正常,打印出hello world,但運行至Print2時,程序崩潰。調用Print1時,并不需要pA的地址,因為Print1的函數地址是固定的。編譯器會給Print1傳入一個this指針,該指針為NULL,但在Print1中該this指針并沒有用到。只要程序運行時沒有訪問不該訪問的內存就不會出錯,因此運行正常。在運行print2時,需要this指針才能得到m_value的值。由于此時this指針為NULL,因此程序崩潰了。
題目(四):運行下面的C++代碼,得到的結果是什么?
class A
{
private:
???????int m_value;
?
public:
???????A(int value)
???????{
???????????????m_value = value;
???????}
???????void Print1()
???????{
???????????????printf("hello world");
???????}
???????virtualvoid Print2()
???????{
???????????????printf("hello world");
???????}
};
?
int _tmain(int argc, _TCHAR* argv[])
{
???????A* pA = NULL;
???????pA->Print1();
???????pA->Print2();
?
???????return 0;
}
分析:答案是Print1調用正常,打印出hello world,但運行至Print2時,程序崩潰。Print1的調用情況和上面的題目一樣,不在贅述。由于Print2是虛函數。C++調用虛函數的時候,要根據實例(即this指針指向的實例)中虛函數表指針得到虛函數表,再從虛函數表中找到函數的地址。由于這一步需要訪問實例的地址(即this指針),而此時this指針為空指針,因此導致內存訪問出錯。
題目(五):C++中靜態成員函數能不能同時也是虛函數?
分析:答案是不能。調用靜態成員函數不要實例。但調用虛函數需要從一個實例中指向虛函數表的指針以得到函數的地址,因此調用虛函數需要一個實例。兩者相互矛盾。
博主何海濤對本博客文章享有版權。網絡轉載請注明出處。整理出版物請和作者聯系。有任何建議或意見,歡迎在評論中告知,或者加我微博http://weibo.com/zhedahht與我交流。
C/C++/C#面試題精選(2)
題目(六):運行下列C++代碼,輸出什么?
struct Point3D
{
???????int x;
???????int y;
???????int z;
};
?
int _tmain(int argc, _TCHAR* argv[])
{
???????Point3D* pPoint = NULL;
???????int offset = (int)(&(pPoint)->z);
?
???????printf("%d", offset);
???????return 0;
}
答案:輸出8。由于在pPoint->z的前面加上了取地址符號,運行到此時的時候,會在pPoint的指針地址上加z在類型Point3D中的偏移量8。由于pPoint的地址是0,因此最終offset的值是8。
&(pPoint->z)的語意是求pPoint中變量z的地址(pPoint的地址0加z的偏移量8),并不需要訪問pPoint指向的內存。只要不訪問非法的內存,程序就不會出錯。
題目(七):運行下列C++代碼,輸出什么?
class A
{
public:
???????A()
???????{
???????????????Print();
???????}
???????virtualvoid Print()
???????{
???????????????printf("A is constructed./n");
???????}
};
?
class B:public A
{
public:
???????B()
???????{
???????????????Print();
???????}
?
???????virtualvoid Print()
???????{
???????????????printf("B is constructed./n");
???????}
};
?
int _tmain(int argc, _TCHAR* argv[])
{
???????A* pA =new B();
???????delete pA;
?
???????return 0;
}
答案:先后打印出兩行:A is constructed. B is constructed.調用B的構造函數時,先會調用B的基類及A的構造函數。然后在A的構造函數里調用Print。由于此時實例的類型B的部分還沒有構造好,本質上它只是A的一個實例,他的虛函數表指針指向的是類型A的虛函數表。因此此時調用的Print是A::Print,而不是B::Print。接著調用類型B的構造函數,并調用Print。此時已經開始構造B,因此此時調用的Print是B::Print。
同樣是調用虛擬函數Print,我們發現在類型A的構造函數中,調用的是A::Print,在B的構造函數中,調用的是B::Print。因此虛函數在構造函數中,已經失去了虛函數的動態綁定特性。
題目(八):運行下列C#代碼,輸出是什么?
namespace ChangesOnString
{
???classProgram
???{
???????staticvoid Main(string[] args)
???????{
???????????String str ="hello";
???????????str.ToUpper();
???????????str.Insert(0," WORLD");
?
???????????Console.WriteLine(str);
???????}
???}
}
答案:輸出是hello。由于在.NET中,String有一個非常特殊的性質:String的實例的狀態不能被改變。如果String的成員函數會修改實例的狀態,將會返回一個新的String實例。改動只會出現在返回值中,而不會修改原來的實例。所以本題中輸出仍然是原來的字符串值hello。
如果試圖改變String的內容,改變之后的值可以通過返回值拿到。用StringBuilder是更好的選擇,特別是要連續多次修改的時候。如果用String連續多次修改,每一次修改都會產生一個臨時對象,開銷太大。
題目(九):在C++和C#中,struct和class有什么不同?
答案:在C++中,如果沒有標明函數或者變量是的訪問權限級別,在struct中,是public的;而在class中,是private的。
???????????????在C#中,如果沒有標明函數或者變量的訪問權限級別,struct和class中都是private的。struct和class的區別是:struct定義值類型,其實例在棧上分配內存;class定義引用類型,其實例在堆上分配內存。
題目(十):運行下圖中的C#代碼,輸出是什么?
namespace StaticConstructor
{
???classA
???{
???????public A(string text)
???????{
???????????Console.WriteLine(text);
???????}
???}
?
???classB
???{
???????staticA a1 =new A("a1");
???????A a2 =newA("a2");
?
???????static B()
???????{
???????????a1 =newA("a3");
???????}
?
???????public B()
???????{
???????????a2 =newA("a4");
???????}
???}
?
???classProgram
???{
???????staticvoid Main(string[] args)
???????{
???????????B b =newB();
???????}
???}
}
答案:打印出四行,分別是a1、a3、a2、a4。
在調用類型B的代碼之前先執行B的靜態構造函數。靜態函數先初始化類型的靜態變量,再執行靜態函數內的語句。因此先打印a1再打印a3。接下來執行B b = new B(),即調用B的普通構造函數。構造函數先初始化成員變量,在執行函數體內的語句,因此先后打印出a2、a4。
博主何海濤對本博客文章享有版權。網絡轉載請注明出處。整理出版物請和作者聯系。有任何建議或意見,歡迎在評論中告知,或者加我微博http://weibo.com/zhedahht與我交流。
C/C++/C#面試題精選(3)
題目(11):運行下圖中的C#代碼,輸出是什么?
namespace StringValueOrReference
{
???class Program
???{
???????internal staticvoid ValueOrReference(Type type)
???????{
???????????String result = "The type " + type.Name;
?
???????????if (type.IsValueType)
???????????????Console.WriteLine(result + " is a value type.");
???????????else
???????????????Console.WriteLine(result + " is a reference type.");
???????}
?
???????internal staticvoid ModifyString(String text)
???????{
???????????text = "world";
???????}
?
???????static void Main(string[] args)
???????{
???????????String text = "hello";
?
???????????ValueOrReference(text.GetType());
???????????ModifyString(text);
?
???????????Console.WriteLine(text);
???????}
???}
}
答案:輸出兩行。第一行是The type String is reference type. 第二行是hello。類型String的定義是public sealed class String {...},既然是class,那么String就是引用類型。
在方法ModifyString里,對text賦值一個新的字符串,此時改變的不是原來text的內容,而是把text指向一個新的字符串"world"。由于參數text沒有加ref或者out,出了方法之后,text還是指向原來的字符串,因此輸出仍然是"hello".
題目(12):運行下圖中的C++代碼,輸出是什么?
#include<iostream>
?
class A
{
private:
???????int n1;
???????int n2;
public:
???????A(): n2(0), n1(n2 + 2)
???????{
???????}
?
???????void Print()
???????{
???????????????std::cout << "n1: " << n1 << ", n2: " << n2 << std::endl;
???????}
};
?
int _tmain(int argc, _TCHAR* argv[])
{
???????A a;
???????a.Print();
?
???????return 0;
}
答案:輸出n1是一個隨機的數字,n2為0。在C++中,成員變量的初始化順序與變量在類型中的申明順序相同,而與它們在構造函數的初始化列表中的順序無關。因此在這道題中,會首先初始化n1,而初始n1的參數n2還沒有初始化,是一個隨機值,因此n1就是一個隨機值。初始化n2時,根據參數0對其初始化,故n2=0。
題目(13):編譯運行下圖中的C++代碼,結果是什么?(A)編譯錯誤;(B)編譯成功,運行時程序崩潰;(C)編譯運行正常,輸出10。請選擇正確答案并分析原因。
#include<iostream>
?
class A
{
private:
???????int value;
?
public:
???????A(int n)
???????{
???????????????value = n;
???????}
?
???????A(A other)
???????{
???????????????value = other.value;
???????}
?
???????void Print()
???????{
???????????????std::cout << value << std::endl;
???????}
};
?
int _tmain(int argc, _TCHAR* argv[])
{
???????A a = 10;
???????A b = a;
???????b.Print();
?
???????return 0;
}
答案:編譯錯誤。在復制構造函數中傳入的參數是A的一個實例。由于是傳值,把形參拷貝到實參會調用復制構造函數。因此如果允許復制構造函數傳值,那么會形成永無休止的遞歸并造成棧溢出。因此C++的標準不允許復制構造函數傳值參數,而必須是傳引用或者常量引用。在Visual Studio和GCC中,都將編譯出錯。
題目(14):運行下圖中的C++代碼,輸出是什么?
int SizeOf(char pString[])
{
???????return sizeof(pString);
}
?
int _tmain(int argc, _TCHAR* argv[])
{
???????char* pString1 = "google";
???????int size1 = sizeof(pString1);
???????int size2 = sizeof(*pString1);
?
???????char pString2[100] = "google";
???????int size3 = sizeof(pString2);
???????int size4 = SizeOf(pString2);
?
???????printf("%d, %d, %d, %d", size1, size2, size3, size4);
?
???????return 0;
}
答案:4, 1, 100, 4。pString1是一個指針。在32位機器上,任意指針都占4個字節的空間。*pString1是字符串pString1的第一個字符。一個字符占一個字節。pString2是一個數組,sizeof(pString2)是求數組的大小。這個數組包含100個字符,因此大小是100個字節。而在函數SizeOf中,雖然傳入的參數是一個字符數組,當數組作為函數的參數進行傳遞時,數組就自動退化為同類型的指針。
題目(15):運行下圖中代碼,輸出的結果是什么?這段代碼有什么問題?
#include<iostream>
?
class A
{
public:
???????A()
???????{??????std::cout << "A is created." << std::endl;???????}
?
???????~A()
???????{??????std::cout << "A is deleted." << std::endl;???????}
};
?
class B :public A
{
public:
???????B()
???????{??????std::cout << "B is created." << std::endl;???????}
?
???????~B()
???????{??????std::cout << "B is deleted." << std::endl;???????}
};
?
int _tmain(int argc, _TCHAR* argv[])
{
???????A* pA = new B();
???????delete pA;
?
???????return 0;
}
答案:輸出三行,分別是:A is created. B is created. A is deleted。用new創建B時,回調用B的構造函數。在調用B的構造函數的時候,會先調用A的構造函數。因此先輸出A is created. B is created.
接下來運行delete語句時,會調用析構函數。由于pA被聲明成類型A的指針,同時基類A的析構函數沒有標上virtual,因此只有A的析構函數被調用到,而不會調用B的析構函數。
由于pA實際上是指向一個B的實例的指針,但在析構的時候只調用了基類A的析構函數,卻沒有調用B的析構函數。這就是一個問題。如果在類型B中創建了一些資源,比如文件句柄、內存等,在這種情況下都得不到釋放,從而導致資源泄漏。
?
博主何海濤對本博客文章享有版權。網絡轉載請注明出處http://blog.csdn.net/cadcisdhht。整理出版物請和作者聯系。有任何建議或意見,歡迎在評論中告知,或者加我微博http://weibo.com/zhedahht與我交流。
C/C++/C#面試題精選(4)
問題(16):運行如下的C++代碼,輸出是什么?
class A
{
public:
???virtualvoid Fun(int number = 10)
???{
???????std::cout <<"A::Fun with number " << number;
???}
};
?
class B:public A
{
public:
???virtualvoid Fun(int number = 20)
???{
???????std::cout <<"B::Fun with number " << number;
???}
};
?
int main()
{
???B b;
???A &a = b;
???a.Fun();
}
答案:輸出B::Fun with number 10。由于a是一個指向B實例的引用,因此在運行的時候會調用B::Fun。但缺省參數是在編譯期決定的。在編譯的時候,編譯器只知道a是一個類型a的引用,具體指向什么類型在編譯期是不能確定的,因此會按照A::Fun的聲明把缺省參數number設為10。
???????????這一題的關鍵在于理解確定缺省參數的值是在編譯的時候,但確定引用、指針的虛函數調用哪個類型的函數是在運行的時候。
問題(17):運行如下的C代碼,輸出是什么?
char* GetString1()
{
???char p[] ="Hello World";
???return p;
}
?
char* GetString2()
{
???char *p ="Hello World";
???return p;
}
?
?
int _tmain(int argc, _TCHAR* argv[])
{
???printf("GetString1 returns: %s. /n", GetString1());
???printf("GetString2 returns: %s. /n", GetString2());
?
???return 0;
}
答案:輸出兩行,第一行GetString1 returns: 后面跟的是一串隨機的內容,而第二行GetString2 returns: Hello World.兩個函數的區別在于GetString1中是一個數組,而GetString2中是一個指針。
當運行到GetString1時,p是一個數組,會開辟一塊內存,并拷貝"Hello World"初始化該數組。接著返回數組的首地址并退出該函數。由于p是GetString1內的一個局部變量,當運行到這個函數外面的時候,這個數組的內存會被釋放掉。因此在_tmain函數里再去訪問這個數組的內容時,結果是隨機的。
當運行到GetString2時,p是一個指針,它指向的是字符串常量區的一個常量字符串。該常量字符串是一個全局的,并不會因為退出函數GetString2而被釋放掉。因此在_tmain中仍然根據GetString2返回的地址得到字符串"Hello World"。
問題(18):運行下圖中C#代碼,輸出的結果是什么?
namespace StaticVariableInAppDomain
{
???[Serializable]
???internalclassA : MarshalByRefObject
???{
???????publicstaticint Number;
?
???????publicvoid SetNumber(int value)
???????{
???????????Number = value;
???????}
???}
?
???[Serializable]
???internalclassB
???{
???????publicstaticint Number;
?
???????publicvoid SetNumber(int value)
???????{
???????????Number = value;
???????}
???}
?
???classProgram
???{
???????staticvoid Main(string[] args)
???????{
???????????String assamblyName =Assembly.GetEntryAssembly().FullName;
???????????AppDomain domain =AppDomain.CreateDomain("NewDomain");
?
???????????A.Number = 10;
???????????String nameOfA =typeof(A).FullName;
???????????A a = domain.CreateInstanceAndUnwrap(assamblyName, nameOfA)asA;
???????????a.SetNumber(20);
???????????Console.WriteLine("Number in class A is {0}",A.Number);
?
???????????B.Number = 10;
???????????String nameOfB =typeof(B).FullName;
???????????B b = domain.CreateInstanceAndUnwrap(assamblyName, nameOfB)asB;
???????????b.SetNumber(20);
???????????Console.WriteLine("Number in class B is {0}",B.Number);
???????}
???}
}
答案:輸出兩行,第一行是Number in class A is 10,而第二行是Number in class B is 20。上述C#代碼先創建一個命名為NewDomain的應用程序域,并在該域中利用反射機制創建類型A的一個實例和類型B的一個實例。我們注意到類型A是繼承自MarshalByRefObject,而B不是。雖然這兩個類型的結構一樣,但由于基類不同而導致在跨越應用程序域的邊界時表現出的行為將大不相同。
?????由于A繼承MarshalByRefObject,那么a實際上只是在缺省的域中的一個代理,它指向位于NewDomain域中的A的一個實例。當a.SetNumber時,是在NewDomain域中調用該方法,它將修改NewDomain域中靜態變量A.Number的值并設為20。由于靜態變量在每個應用程序域中都有一份獨立的拷貝,修改NewDomain域中的靜態變量A.Number對缺省域中的靜態變量A.NewDomain沒有任何影響。由于Console.WriteLine是在缺省的應用程序域中輸出A.Number,因此輸出仍然是10。
???B只從Object繼承而來的類型,它的實例穿越應用程序域的邊界時,將會完整地拷貝實例。在上述代碼中,我們盡管試圖在NewDomani域中生成B的實例,但會把實例b拷貝到缺省的域。此時,調用b.SetNumber也是在缺省的域上進行,它將修改缺省的域上的A.Number并設為20。因此這一次輸出的是20。
問題(19):運行下圖中C代碼,輸出的結果是什么?
int _tmain(int argc, _TCHAR* argv[])
{
???char str1[] ="hello world";
???char str2[] ="hello world";
?
???char* str3 ="hello world";
???char* str4 ="hello world";
?
???if(str1 == str2)
???????printf("str1 and str2 are same./n");
???else
???????printf("str1 and str2 are not same./n");
?
???if(str3 == str4)
???????printf("str3 and str4 are same./n");
???else
???????printf("str3 and str4 are not same./n");
?
???return 0;
}
答案:輸出兩行。第一行是str1 and str2 are not same,第二行是str3 and str4 are same。
str1和str2是兩個字符串數組。我們會為它們分配兩個長度為12個字節的空間,并把"hello world"的內容分別拷貝到數組中去。這是兩個初始地址不同的數組,因此比較str1和str2的值,會不相同。str3和str4是兩個指針,我們無需為它們分配內存以存儲字符串的內容,而只需要把它們指向"hello world“在內存中的地址就可以了。由于"hello world”是常量字符串,它在內存中只有一個拷貝,因此str3和str4指向的是同一個地址。因此比較str3和str4的值,會是相同的。
問題(20):運行下圖中C#代碼,輸出的結果是什么?并請比較這兩個類型各有什么特點,有哪些區別。
namespace Singleton
{
???publicsealedclass Singleton1
???{
???????private Singleton1()
???????{
???????????Console.WriteLine("Singleton1 constructed");
???????}
???????publicstaticvoid Print()
???????{
???????????Console.WriteLine("Singleton1 Print");
???????}
???????privatestaticSingleton1 instance =new Singleton1();
???????publicstaticSingleton1 Instance
???????{
???????????get
???????????{
???????????????return instance;
???????????}
???????}
???}
?
???publicsealedclass Singleton2
???{
???????Singleton2()
???????{
???????????Console.WriteLine("Singleton2 constructed");
???????}
???????publicstaticvoid Print()
???????{
???????????Console.WriteLine("Singleton2 Print");
???????}
???????publicstaticSingleton2 Instance
???????{
???????????get
???????????{
???????????????returnNested.instance;
???????????}
???????}
???????classNested
???????{
???????????static Nested() { }
?
???????????internalstaticreadonly Singleton2 instance =new Singleton2();
???????}
???}
?
???classProgram
???{
???????staticvoid Main(string[] args)
???????{
???????????Singleton1.Print();
???????????Singleton2.Print();
???????}
???}
}
答案:輸出三行:第一行“Singleton1 constructed”,第二行“Singleton1 Print”,第三行“Singleton2 Print”。
當我們調用Singleton1.Print時,.NET運行時會自動調用Singleton1的靜態構造函數,并初始化它的靜態變量。此時會創建一個Singleton1的實例,因此會調用它的構造函數。Singleton2的實例是在Nested的靜態構造函數里初始化的。只有當類型Nested被使用時,才回觸發.NET運行時調用它的靜態構造函數。我們注意到我們只在Sington2.Instance里面用到了Nested。而在我們的代碼中,只調用了Singleton2.Print。因此不會創建Singleton2的實例,也不會調用它的構造函數。
這兩個類型其實都是單例模式(Singleton)的實現。第二個實現Singleton2只在真的需要時,才會創建實例,而第一個實現Singleton1則不然。第二個實現在空間效率上更好。
博主何海濤對本博客文章享有版權。網絡轉載請注明出處http://blog.csdn.net/cadcisdhht。整理出版物請和作者聯系。有任何建議或意見,歡迎在評論中告知,或者加我微博http://weibo.com/zhedahht與我交流。
C/C++/C#面試題精選(5)
問題(21):C#是一門托管語言,那么是不是說明只要用C#,就能保證不會出現內存泄露和其他資源泄漏?如果不是,在哪些情況下可能會出現泄漏?
答案:C#不能保證沒有資源泄漏。比如如下幾種情況可能會造成資源泄漏:(1) 調用Nativecode,比如用P/Invoke或者調用COM;(2) 讀寫文件時的,沒有及時closestream, 或者ADO.NET連數據庫時,沒有及時關閉連接,也算資源泄漏?(3)注冊事件后沒有remove,導致publisher和subscriber的強依 賴,垃圾回收可能會被推遲;(4).NET還定義了一些方法直接申請非托管內存,比如Marshal.AllocHGlobal和Marshal.AllocCoTaskMem。通過這種方式得到的內存,如果沒有及時釋放,也會造成內存泄露。
問題(22):下面的兩段C#有哪些不同?
- static?void?CatchException1()??
- {??
- ????try??
- ????{??
- ????????Function();??
- ????}??
- ????catch??
- ????{??
- ????????throw;??
- ????}??
- }??
- ??
- static?void?CatchException2()??
- {??
- ????try??
- ????{??
- ????????Function();??
- ????}??
- ????catch?(Exception?e)??
- ????{??
- ????????throw?e;??
- ????}??
- }??
問題(23):運行下圖中的C++代碼,打印出的結果是什么?
- bool?Fun1(char*?str)??
- {??
- ????printf("%s\n",str);??
- ????return?false;??
- }??
- ??
- bool?Fun2(char*?str)??
- {??
- ????printf("%s\n",str);??
- ????return?true;??
- }??
- ??
- int?_tmain(int?argc,?_TCHAR*?argv[])??
- {??
- ????bool?res1,res2;??
- ????res1?=?(Fun1("a")&&?Fun2("b"))?||?(Fun1("c")?||?Fun2("d"));??
- ????res2?=?(Fun1("a")&&?Fun2("b"))?&&(Fun1("c")?||?Fun2("d"));??
- ??
- ????return?res1||?res2;??
- }??
在C/C++中,與、或運算是從左到右的順序執行的。在計算rest1時,先計算Fun1(“a”)&& Func2(“b”)。首先Func1(“a”)打印出內容為a的一行。由于Fun1(“a”)返回的是false,無論Func2(“b”)的返回值是true還是false,Fun1(“a”)&& Func2(“b”)的結果都是false。由于Func2(“b”)的結果無關重要,因此Func2(“b”)會略去而不做計算。接下來計算Fun1(“c”)|| Func2(“d”),分別打印出內容c和d的兩行。
??????????????? 在計算rest2時,首先Func1(“a”)打印出內容為a的一行。由于Func1(“a”)返回false,和前面一樣的道理,Func2(“b”)會略去不做計算。由于Fun1(“a”)&& Func2(“b”)的結果是false,不管Fun1(“c”)&& Func2(“d”)的結果是什么,整個表達式得到的結果都是false,因此Fun1(“c”) || Func2(“d”)都將被忽略。
問題(24):運行下面的C#代碼,打印出來的結果是什么?
- struct?Person??
- {??
- ????public?string?Name;??
- ??
- ????public?override?string?ToString()??
- ????{??
- ????????return?Name;??
- ????}??
- }??
- ??
- class?Program??
- {??
- ????static?void?Main(string[]args)??
- ????{??
- ????????ArrayListarray?=?new?ArrayList();??
- ????????Personjim?=?new?Person(){Name?=?"Jim"};??
- ????????array.Add(jim);??
- ??
- ????????Personfirst?=?(Person)array[0];??
- ????????first.Name?=?"Peter";??
- ??
- ????????Console.WriteLine(array[0].ToString());??
- ????}??
- }??
問題(25):運行下面的C++代碼,打印的結果是什么?
- class?Base??
- {??
- public:??
- ????voidprint()?{?doPrint();}??
- ??
- private:??
- ????virtual?void?doPrint()?{cout?<<?"Base::doPrint"?<<?endl;}??
- };??
- ??
- class?Derived?:?public?Base??
- {??
- private:??
- ????virtual?void?doPrint()?{cout?<<?"Derived::doPrint"?<<?endl;}??
- };??
- ??
- int?_tmain(int?argc,?_TCHAR*?argv[])??
- {??
- ????Base?b;??
- ????b.print();??
- ??
- ????Derived?d;??
- ????d.print();??
- ??
- ????return?0;??
- }??
答案:輸出兩行,分別是Base::doPrint和Derived::doPrint。在print中調用doPrint時,doPrint()的寫法和this->doPrint()是等價的,因此將根據實際的類型調用對應的doPrint。所以結果是分別調用的是Base::doPrint和Derived::doPrint2。如果感興趣,可以查看一下匯編代碼,就能看出來調用doPrint是從虛函數表中得到函數地址的。
博主何海濤對本博客文章享有版權。網絡轉載請注明出處http://blog.csdn.net/cadcisdhht。整理出版物請和作者聯系。有任何建議或意見,歡迎在評論中告知,或者加我微博http://weibo.com/zhedahht與我交流。