有點意思的C/C++問題及解答:6-10
問題 6:非C++內建型別A 和B,在哪幾種情況下B 能隱式轉化為A?
(1)class A { ...... }; class B : public A { ……} ; // B 公有繼承自A,可以是間接繼承的
(2)class A { ...... }; class B { operator A() { return A::A(); } ......}; // B 實現了到A 的型別轉化
(3)class A { A( const B& ); } ; // A 實現了non-explicit 的引數為B(可以有其他帶預設值的引數)建構函式
提供隱式轉換時,應避免出現二義性。比如上面的幾種方式,如果即定義了(2)又定義了(3),就存在二義性。因為編譯器可以用兩種方式將B轉換為A,一是用A的建構函式,另一種是用B的型別轉換符。最佳實踐:避免二義性最好的辦法是避免編寫互相提供隱式轉換的成對的類。《C++ Primer》。
問題 7:下面這個程式有什麼問題?
[cpp] view plain copy print ?
- #include <iostream>
- using namespace std;
- class A
- {
- private:
- int
- public:
- A(int n) { value = n; }
- A(A other){ value = other.value; }
- void Print(){ cout << value << endl; }
- };
- int main()
- {
- A a = 10;
- A b = a;
- b.Print();
- return 0;
- }
編譯錯誤, A(A other){} 這個複製建構函式定義有誤,該函式的形參只能是本類型別物件的引用,常用const修飾《C++ Primer》。應該改成這樣 A(const A&other) { value = other.value; }。複製建構函式可以用於:
(1)根據另一個同類型的物件顯示或隱式初始化一個物件
string s = "hello";
首先會呼叫接受一個C風格字串形參的string建構函式,建立臨時物件,然後呼叫複製建構函式,將s初始化為那個臨時物件的副本。
(2)複製一個物件,將它作為實參傳遞給一個函式
void foo(string s);
(3)從函式返回複製一個物件
string foo();
(4)初始化容器中的元素。
vector<string> svec(5);
首先呼叫string預設建構函式建立一個臨時值,然後將這個臨時值複製給svec的每一個元素,即呼叫string複製建構函式。
(5)根據元素初始化列表初始化陣列元素
比如下面這個函式,初始化陣列a 時呼叫的是複製建構函式。而初始化陣列b時呼叫的是預設建構函式。而初始化陣列c時,由於它有4個元素,而初始化列表只有2個,因此前兩個元素的初始化呼叫複製建構函式,後兩個元素的初始化呼叫預設建構函式。
[cpp] view plain copy print ?
- #include <iostream>
- using namespace std;
- class A
- {
- public:
- A() { value=1;cout<<"A Default Constructor"<<endl;}
- A(int v) {value=v;cout<<"A Copy Constructor"<<endl;}
- int value;
- };
- int main()
- {
- A a[2]={1,2};
- A b[2];
- A c[4]={1,2};
- return 0;
- }
問題8:建構函式、靜態函式、行內函數可不可以是虛擬函式?
建構函式和靜態函式不可以、行內函數可以
建構函式不可以為虛擬函式:虛擬函式的呼叫是通過一個虛指標來實現的,而這個虛指標是在構造過程中設定的。
靜態函式不可為虛擬函式:靜態函式的呼叫是不需要例項的,而虛擬函式需要通過從一個例項中獲取虛指標,進而獲取函式的地址,從而實現動態繫結。
行內函數可以:內聯是在編譯階段用程式碼換呼叫,而虛擬函式是在執行期動態繫結。雖然可以,但是一般不會這樣做,代價太大了。
問題9:解構函式必須是虛擬函式嗎?
答案是並不是必須的。如果類被設計成能被繼承,解構函式必須是虛擬函式。否則可以不為虛擬函式。解構函式寫成虛擬函式主要是為了在實現多型時不造成記憶體洩露。比如下面這段程式,B的解構函式不會被呼叫,如果將A的解構函式寫成虛擬函式,則B的解構函式可以被呼叫。
- #include <iostream>
- using namespace std;
- class A
- {
- public:
- A() {cout<<"A Constructor\n";}
- ~A() {cout<<"A Destructor\n";}
- };
- class B:public A
- {
- public:
- B() { cout<<"B Constructor\n";}
- ~B() { cout<<"B Destructor\n";}
- };
- int main()
- {
- A *pa=new B();
- delete pa;
- return 0;
- }
- #include <iostream>
- using namespace std;
- struct Point3D
- {
- int x;
- int y;
- int z;
- };
- int main()
- {
- Point3D* pPoint = NULL;
- int offset = (int)(&(pPoint->z));
- cout<<offset<<endl;
- return 0;
- }
如果是&(pPoint->x)和&(pPoint->y),則結果為0和4。
如果pPoint不為空,例如 Point3D* pPoint = new Point3D; 三個成員變數的地址依次相差4。