C++面試題1:建構函式和虛構函數中能否調用虛函數?,建構函式函數
C++面試題1:建構函式和虛構函數中能否調用虛函數?
- 建構函式跟虛構函數裡面都可以調用虛函數,編譯器不會報錯。
- C++ primer中說到最好別用
- 由於類的構造次序是由基類到衍生類別,所以在建構函式中調用虛函數,虛函數是不會呈現出多態的
- 類的析構是從衍生類別到基類,當調用繼承層次中某一層次的類的解構函式時意味著其衍生類別部分已經析構掉,所以也不會呈現多態
- 因此如果在基類中聲明的純虛函數並且在基類的解構函式中調用之,編譯器會發生錯誤。
class Base{public: Base() { Fuction(); } virtual void Fuction() { cout << "Base::Fuction" << endl; }};class A : public Base{public: A() { Fuction(); } virtual void Fuction() { cout << "A::Fuction" << endl; }};// 這樣定義一個A的對象,會輸出什嗎?A a;首先調用應該是沒有問題的,但是得到的結果呢?很多人會說輸出:A::Fuction A::Function如果是按照上面的情形進行輸出的話,那就是說在構造Base的時候,也就是在Base的建構函式中調用Fuction的時候,調用了子類A的Fuction,而實際上A還沒有開始構造,這樣函數的行為就是完全不可預測的,因此顯然不是這樣,實際的輸出結果是:Base::FuctionA::Fuction
例子2:
#include <iostream>using namespace std;class A{public: A() { cout << "A建構函式"; Test(); } ~A() { cout << "A解構函式"; cout << "A::Test()" << endl; } virtual void Test() { cout << "A::Test()" << endl; }};class B:public A{public: B() { cout << "B建構函式"; Test(); } ~B() { cout << "B解構函式"; Test(); } virtual void Test() { cout << "B::Test()" << endl; }};int _tmain(int argc, _TCHAR* argv[]){ A* pA = new B(); 調用建構函式 輸出A建構函式:A::Test() B調用建構函式B::Test() cout << "動態調用:"; pA->Test(); 原指標類型是PA,實際指標類型是B,由於是虛函數,所以按實際類型調用 B::Test( delete pA; 由於A的析構不是虛函數,所以按照原類型指標調用,如果在A的解構函式中加上virtual 則輸出為B解構函式B::Test A解構函式A::Test return 0;}
例子3:
#include<iostream>using namespace std; class A { public: void virtual f() { cout<<"A"<<endl; } }; class B : public A { public: void virtual f() { cout<<"B"<<endl; } }; int main () { A* pa=new A(); pa->f(); 這個很明顯A B* pb=(B*)pa; pb->f(); 這個強制將pa複製到pb,所以pb指向A delete pa,pb; 刪除pa,pb所指向的地址,但是pa、pb指標並沒有刪除,懸浮指標 pa=new B(); pa->f(); B pb=(B*)pa; pb->f(); B return 0; }總結:虛函數根據指標實際類型調用,其他函數都是根據原類型調用。
建構函式與解構函式中可以否調用虛函數
1.
如果一個類不可能是基類就不要申明解構函式為虛函數,虛函數是要耗費空間的。
2. 解構函式的異常退出會導致析構不完全,從而有記憶體泄露。最好是提供一個管理類,在管理類中提供一個方法來析構,調用者再根據這個方法的結果決定下一步的操作
3. 在建構函式不要調用虛函數。在基類構造的時候,虛函數是非虛,不會走到衍生類別中,既是採用的靜態繫結。顯然的是:當我們構造一個子類的對象時,先調用基類的建構函式,構造子類中基類部分,子類還沒有構造,還沒有初始化,如果在基類的構造中調用虛函數,如果可以的話就是調用一個還沒有被初始化的對象,那是很危險的,所以C++中是不可以在構造父類對象部分的時候調用子類的虛函數實現。但是不是說你不可以那麼寫程式,
你這麼寫,編譯器也不會報錯。4.在解構函式中也不要調用虛函數。在析構的時候會首先調用子類的解構函式,析構掉對象中的子類部分,然後在調用基類的解構函式析構基類部分,如果在基類的解構函式裡面調用虛函數,會導致其調用已經析構了的子類對象裡面的函數,這是非常危險的。5. 記得在寫衍生類別的拷貝函數時,調用基類的拷貝函數拷貝基類的部分,不能忘記了
建構函式不可以是虛函數,可是為何還有虛建構函式這一說?
虛函數,有的語言裡也叫動態函數(DYNAMIC,相對於VIRTUAL),這裡的虛,不是沒有,而是說,調用對象的類型決定具體的函數。
對象要通過建構函式來創造(好像廢話),所以建構函式必須有內容,不能是空的。(內容為空白的虛函數,有叫抽象函數的,也有叫純虛函數的)
但不同的子類有不同的處理,所以建構函式應該是“virtual”的,即是說,它應該能被子類改寫。