1. 從存儲空間角度,虛函數相應一個指向vtable虛函數表的指針,這大家都知道,但是這個指向vtable的指針事實上是存儲在對象的內存空間的。問題出來了,假設構造函數是虛的,就須要通過 vtable來調用,但是對象還沒有實例化,也就是內存空間還沒有,怎么找vtable呢?所以構造函數不能是虛函數。
虛函數與非虛函數對照
l 每一個對象都將增大,增大量為存儲虛函數表指針的大小;
l 對於每一個類,編譯器都創建一個虛函數地址表;
l 對於每一個函數調用,都須要運行一項額外的操作,即到虛函數表中查找地址。
盡管非虛函數比虛函數效率稍高,單不具備動態聯編能力
二、為什么基類的析構函數是虛函數?
在實現多態時,當用基類操作派生類,在析構時防止僅僅析構基類而不析構派生類的狀況發生。
以下轉自網絡:源地址 http://blog.sina.com.cn/s/blog_7c773cc50100y9hz.html
a.第一段代碼
#include<iostream> using namespace std; class ClxBase{ public: ClxBase() {}; ~ClxBase() {cout << "Output from the destructor of class ClxBase!" << endl;}; void DoSomething() { cout << "Do something in class ClxBase!" << endl; }; }; class ClxDerived : public ClxBase{ public: ClxDerived() {}; ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; }; void DoSomething() { cout << "Do something in class ClxDerived!" << endl; }; }; int main(){ ClxDerived *p = new ClxDerived; p->DoSomething(); delete p; return 0; }
執行結果:
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
Output from the destructor of class ClxBase!
這段代碼中基類的析構函數不是虛函數,在main函數中用繼承類的指針去操作繼承類的成員,釋放指針P的過程是:先釋放繼承類的資源,再釋放基類資源.
b.第二段代碼
#include<iostream> using namespace std; class ClxBase{ public: ClxBase() {}; ~ClxBase() {cout << "Output from the destructor of class ClxBase!" << endl;}; void DoSomething() { cout << "Do something in class ClxBase!" << endl; }; }; class ClxDerived : public ClxBase{ public: ClxDerived() {}; ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; }; void DoSomething() { cout << "Do something in class ClxDerived!" << endl; } }; int main(){ ClxBase *p = new ClxDerived; p->DoSomething(); delete p; return 0; }
輸出結果:
Do something in class ClxBase!
Output from the destructor of class ClxBase!
這段代碼中基類的析構函數相同不是虛函數,不同的是在main函數中用基類的指針去操作繼承類的成員,釋放指針P的過程是:僅僅是釋放了基類的資源,而沒有調用繼承類的析構函數.調用 dosomething()函數運行的也是基類定義的函數.
普通情況下,這種刪除僅僅可以刪除基類對象,而不能刪除子類對象,形成了刪除一半形象,造成內存泄漏.
在公有繼承中,基類對派生類及其對象的操作,僅僅能影響到那些從基類繼承下來的成員.假設想要用基類對非繼承成員進行操作,則要把基類的這個函數定義為虛函數.
析構函數自然也應該如此:假設它想析構子類中的又一次定義或新的成員及對象,當然也應該聲明為虛的.
c.第三段代碼:
#include<iostream> using namespace std; class ClxBase{ public: ClxBase() {}; virtual ~ClxBase() {cout << "Output from the destructor of class ClxBase!" << endl;}; virtual void DoSomething() { cout << "Do something in class ClxBase!" << endl; }; }; class ClxDerived : public ClxBase{ public: ClxDerived() {}; ~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; }; void DoSomething() { cout << "Do something in class ClxDerived!" << endl; }; }; int main(){ ClxBase *p = new ClxDerived; p->DoSomething(); delete p; return 0; }
執行結果:
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
Output from the destructor of class ClxBase!
這段代碼中基類的析構函數被定義為虛函數,在main函數中用基類的指針去操作繼承類的成員,釋放指針P的過程是:僅僅是釋放了繼承類的資源,再調用基類的析構函數.調用dosomething()函數運行的也是繼承類定義的函數.
假設不須要基類對派生類及對象進行操作,則不能定義虛函數,由於這樣會添加內存開銷.當類里面有定義虛函數的時候,編譯器會給類加入一個虛函數表,里面來存放虛函數指針,這樣就會添加類的存儲空間.所以,僅僅有當一個類被用來作為基類的時候,才把析構函數寫成虛函數.