衆所周知,C++虛函數是一大難點,也是面試過程當中必考部分。這次,從虛函數的相關概念、虛函數表、純虛函數、再到虛繼承等等跟虛函數相關部分,作一個比較細緻的整理和複習。面試
cpp //強行調用基類中定義的函數版本而無論baseP的動態類型究竟是什麼 double price = basePtr->Base::net_price();
cpp //Base 聲明瞭純虛函數,而 Derive將覆蓋該函數 Base b; //錯誤,不能定義Base的對象 Derive d; //正確,Derive中沒有純虛函數
基類定義以下所示:
```cpp
class Base{
public:
Base()
:a(0), b(0), c('\0'){}函數
virtual void fun1(){ cout << "Base::fun1()" << endl; } virtual void fun2(){ cout << "Base::fun2()" << endl; }
private:
int a;
double b;
char c;
};
```
類Base對象其內存佈局方式爲:
佈局
考慮繼承的狀況,以下所示:
```cpp
class Derive : public Base{
public:
Derive()
:Base(),d(0), f(0){}學習
virtual void fun1(){ cout << "Derive::fun1()" << endl; } virtual void fun3(){ cout << "Derive::fun3()" << endl; }
private:
int d;
float f;
};
```
類Derive對象其內存佈局以下所示:
.net
其實Derive對象的內存佈局是能夠這樣理解,可是也不是很準確。
如上所示,在Derive的定義中,我從新實現了Base的fun1(),直接繼承了Base::fun2(),再新定義了 Derive::fun3()
經過調試,即上面的右圖發現,在Derive的對象中,可以看到的虛函數表是從Base繼承而來的,其中裏面覆寫fun1(),繼承了fun2(),可是並無fun3()的函數指針。因此按照上邊的左圖,給出內存佈局的話,可能會有一些誤導。指針
由上可知,派生類若是沒有定義新的虛函數,則直接繼承虛類的虛函數表,並在其中作相應修改。若是定義了新的虛函數,不止要繼承虛類的,還要維護本身的。
因此上面的Derive的內存佈局的另外一種狀況多是:
調試
下面給出一個多重繼承的討論狀況:
```cpp
class Base1{
public:
Base1()
{}code
virtual void fun1(){ cout << "Base1::fun1()" << endl; } virtual void fun2(){ cout << "Base1::fun2()" << endl; }
};對象
class Base2{
public:
Base2(){}blog
virtual void fun3(){ cout << "Base2::fun3()" << endl; } virtual void fun4(){ cout << "Base2::fun4()" << endl; }
};
class Derive : public Base1, public Base2(){
public:
Derive()
:Base1(), Base2() {}
virtual void fun2(){ cout << "Derive::fun2()" << endl; } virtual void fun3(){ cout << "Derive::fun3()" << endl; } virtual void fun5(){ cout << "Derive::fun5()" << endl; }
}
```
Derive的對象內存佈局以下:
這是篇好文章C++ 多繼承和虛繼承的內存佈局,雖然不是很懂,可是確實有幫助。下面在給出一些相關概念。
:C++ 對象的內存佈局(下)關於虛擬繼承的例子部從這篇文章學習,推薦。
很明顯: sizeof(Base) = 8
緣由:帶有虛函數的類具備虛函數指針,而後再加上int
乍一看 sizeof(Base) = 16, 其實應該是 sizeof(Base) = 24
爲何呢, 由於前面關於字節對齊中,提到過 類的隱藏對象不能影響其後的數據成員的對齊,因此通常隱藏對象都是最大對齊字節的整數倍。此時 最大對齊爲8,因此 虛函數表指針佔4個字節,但須要填充4個。而後 int 佔 4 個,再填充 4 個,最後double佔8個。一共24個。
class A {
int a;
virtual ~A(){}
};
class B:virtual public A{
virtual void funB(){}
};
class C:virtual public A{
virtual void funC(){}
};
class D:public B,public C{
virtual void funD(){}
};
sizeof(A) = 8
sizeof(B) = 12
sizeof(C) = 12
sizeof(D) = 16
A 中是虛函數指針 + int
B、C 虛繼承A,大小爲 A + 指向虛基類的指針,B、C雖然新定義了虛函數,可是共享A中的虛函數指針。
D 因爲是普通繼承 B、C,可是因爲 B 、C是虛繼承,因此D中保留A的一個副本。因此大小爲 A + B指向虛基類的指針 + C指向虛基類的指針
```
最後給出一個上面討論 2 的具體實例。在VS2013下查看內存佈局以下:
上圖中沒有搞懂的部分,應該是隨機數,系統隨機的。不用管。