C++学习笔记-----不要在构造函数和析构函数中调用虚函数
考慮下面的程序:
#include <iostream> using namespace std;class Base { public:Base() { cout << "Base Construct" << endl; func(); }~Base() { cout << "Base Destroy" << endl; }void toG() { g(); }virtual void func() { cout << "Base virtual function" << endl; } private:virtual void g() { cout << "Base Private virtual" << endl; } };class Derive : public Base { public:Derive() : Base() { cout << "Derive Construct" << endl; func(); }~Derive() { cout << "Derive Destroy" << endl; }void func() { cout << "Derive virtual function" << endl; } private:void g() { cout << "Derive Private virtual" << endl; } };int main() {Derive d;cout << endl;d.toG();cout << endl;cin.get();return 0; }如你所見,我們定義了一個(gè)基類Base,又定義了一個(gè)派生類Derive并以public的形式繼承它。
在構(gòu)造函數(shù)中分別輸出不同的字符串常量然后調(diào)用虛函數(shù)func()。
另外在基類中又存在一個(gè)toG()函數(shù)用來調(diào)用函數(shù)內(nèi)部的虛函數(shù)g(),通常這里的g函數(shù)應(yīng)該定義在私有成員作用域中,為了實(shí)現(xiàn)用同一個(gè)基類接口調(diào)用不用的派生類虛函數(shù)的目的。
讓我們來看一下程序的運(yùn)行結(jié)果:
發(fā)現(xiàn)什么不對(duì)的地方了嗎,如果沒有,我們一步步分析:
Derive d;首先像往常一樣,定義了一個(gè)派生類的實(shí)例化對(duì)象,構(gòu)造的順序是從基類到派生類。先調(diào)用基類的構(gòu)造函數(shù)輸出“Base Construct”,又調(diào)用了基類的func()函數(shù)輸出了“Base virtual function“,到這里基類部分構(gòu)造完成,開始進(jìn)入派生類的構(gòu)造階段,輸出”Derive Construct“,又調(diào)用了派生類的func()函數(shù)輸出了”Derive virtual function"。 d.toG();我們用派生類的對(duì)象調(diào)用基類的接口函數(shù)toG(),在函數(shù)內(nèi)部調(diào)用了派生類的g()函數(shù)輸出”Derive Private virtual“。比較上面的結(jié)果,不難發(fā)現(xiàn),同樣是在基類的作用域下,func()函數(shù)調(diào)用的是基類的,g()函數(shù)卻調(diào)用的是派生類的。
這正是問題所在,想一想,上述哪種調(diào)用結(jié)果是符合預(yù)期的,為什么會(huì)出現(xiàn)這樣的不一致?
涉及到虛函數(shù)調(diào)用的問題基本上都可以用虛函數(shù)表來分析解決,在定義派生類對(duì)象的時(shí)候,因?yàn)閒unc和g兩個(gè)函數(shù)都是虛函數(shù)且在派生類中都重新定義了,所以虛函數(shù)表中函數(shù)的地址都會(huì)被覆蓋成派生類的虛函數(shù)的地址,也就是說只要我們用派生類的對(duì)象調(diào)用這兩個(gè)函數(shù),會(huì)在虛函數(shù)表中尋找最佳匹配的函數(shù)獲得它的函數(shù)地址并調(diào)用它,不管作用域是在基類還是在派生類,都會(huì)調(diào)用派生類的相應(yīng)函數(shù)。
然而func()的調(diào)用好像并非如此,這就是為什么說不要在構(gòu)造函數(shù)中調(diào)用虛函數(shù)的原因。
至于為什么會(huì)出現(xiàn)這樣的結(jié)果,我們知道,在派生類對(duì)象的構(gòu)造階段是先構(gòu)造其基類部分的,也就是可以理解成在構(gòu)造基類的時(shí)候派生類的部分還不存在,試想,我們又怎么能去調(diào)用一個(gè)并不存在的東西的子成員呢?編譯器也正是假設(shè)在構(gòu)造階段派生類對(duì)象還不存在,所以不會(huì)去調(diào)用派生類的虛函數(shù)。
歸結(jié)一點(diǎn),不要在構(gòu)造函數(shù)中調(diào)用虛函數(shù),更準(zhǔn)確的說法是不要在基類的構(gòu)造函數(shù)中調(diào)用虛函數(shù),因?yàn)榫幾g器調(diào)用的永遠(yuǎn)都是屬于基類的那個(gè)虛函數(shù)而不是派生類的。
同樣的原因可以解釋析構(gòu)函數(shù),因?yàn)槲鰳?gòu)函數(shù)的調(diào)用時(shí)從派生類到基類的,也就是說先銷毀派生類獨(dú)立的那部分,在向上銷毀它的基類。那么如果在基類的析構(gòu)函數(shù)中調(diào)用了虛函數(shù),此時(shí)派生類部分已經(jīng)被銷毀了,又不存在了,那調(diào)用的虛函數(shù)永遠(yuǎn)都是基類的那個(gè)。
總結(jié)
以上是生活随笔為你收集整理的C++学习笔记-----不要在构造函数和析构函数中调用虚函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++学习笔记-----operator
- 下一篇: C++学习笔记-----在重载的赋值运算