1.虛函數的代價
1)帶有虛函數的每個類會產生一個虛函數表,用來存儲虛成員函數的指針
2)帶有虛函數的每個類都會有一個指向虛函數表的指針
3)不再是內斂函數,因為內斂函數可以在編譯階段進行替代,而虛函數表示等待,在運行階段才能確定到達采用哪種函數,所以虛函數不是內斂函數
2.那些函數不能是虛函數?
1)構造函數:對象的虛函數表指針需要通過構造函數初始化
2)內聯函數:內聯函數可以在編譯階段進行函數體的替換,而虛函數需要在運行期間進行確定
3)靜態函數:靜態函數不屬於對象而屬於類,因為靜態成員函數沒有this指針,所以無法訪問對象的虛表指針,也就
無法訪問類的虛函數表,將靜態函數設置成虛函數也就沒有任何意義,所以c++語法不支持將靜態函數設置成虛函數
4)友元函數:友元函數不屬於類,也不能被繼承,沒有繼承特性的函數沒有虛函數的說法
5)類外的普通函數:類外普通函數不是類的成員函數,同樣不具備繼承特性,也就沒有虛函數的說法
3.虛函數和純虛函數的區別?
1)純虛函數只有定義,沒有實現,虛函數既有定義,又有實現
2)含有純虛函數的類不能定義對象,含有虛函數的類可以定義對象
4.菱形繼承的內存結構?如何解決菱形繼承存在的問題?

#include <iostream> using namespace std; class A { public: int x; virtual int getx() { return x; } }; class B:public A{}; class C:public A{}; class D:public B,public C{}; int main() { D d; //以下代碼不會被任何編譯器通過,因為存在菱形繼承問題 cout<<d.getx()<<endl; }
1)菱形繼承的內存結構:現在有A,B,C,D四個類,B,C分別繼承A類,D通過多重繼承繼承了BC兩個類,現在D類中有兩個getx(),D類不知道調用哪一個getx()
2)菱形繼承的解決辦法:虛繼承
BC類都用Virtual標注,保證只有一個getx()被創建

#include <iostream> using namespace std; class A { public: int x=5; virtual int getx() { return x; } }; class B:virtual public A{}; class C:virtual public A{}; class D:public B,public C{}; int main() { D d; cout<<d.getx()<<endl; }
5.虛析構函數的作用?父類的析構函數為什么一定要設置成虛函數?
父類虛析構函數就是為了避免內存泄漏,防止子類內存得不到釋放造成內存泄漏
1.當父類的析構函數不聲明成虛析構函數時,當子類繼承父類,父類指針指向子類對象,delete掉父類指針,只會調動父類的析構函數,而不會調用子類的析構函數,從而造成子類對象內存泄漏
2.當父類的析構函數聲明成虛析構函數時,當子類繼承父類,父類指針指向子類對象,delete掉父類指針,先調動父類的析構函數,然后調用子類的析構函數,不存在子類對象內存泄漏的問題
只要存在繼承關系,則父類的虛函數必須定義成虛函數!
6.構造函數和析構函數中為什么不可以調用虛函數?
背景知識:
1.構造子類對象時,首先調用父類構造函數初始化對象的父類部分,在執行父類的構造函數時,對象的子類部分都是未初始化的,實際上此時對象還不是一個子類對象
2.析構子類對象時,先析構子類部分,然后按照構造順序逆序析構父類部分
所以在運行子類的構造和析構函數時,對象都是不完整的,為了適應這種不完整,編譯器視對象類型為當前構造或析構函數所在類的類型,由此造成的結構就是:在父類的構造或析構函數中,會將子類對象當作父類對象看待
在這樣的背景下
如果我們在父類的構造或析構函數中調用虛函數,調用的往往是當前類的虛函數,達不到多態的效果,跟普通函數調用沒有區別
7.構造函數為什么不能為虛函數?什么情況下析構函數必須為虛函數?
1)因為虛函數表指針必須在構造函數中初始化,所以構造函數不能為虛函數!
2)當存在繼承關系時,父類的析構函數必須為虛函數,這樣在父類指針指向子類對象,delete父類指針時,子類對象才不會內存泄漏