前言
近期有不少同學(xué)私信我詢(xún)問(wèn)關(guān)于C++ 虛表和虛函數(shù)的相關(guān)問(wèn)題,于是就打算寫(xiě)一篇關(guān)于C++虛函數(shù)和虛表的原理文章有助于大家更好的去理解和學(xué)習(xí)。
虛函數(shù)
概念
虛函數(shù)是一種在基類(lèi)中用virtual關(guān)鍵字聲明的函數(shù),并在一個(gè)或多個(gè)派生類(lèi)中再定義的函數(shù)。虛函數(shù)的特點(diǎn)是,只要定義一個(gè)基類(lèi)的指針,就可以指向派生類(lèi)的對(duì)象。
[注:無(wú)虛函數(shù)時(shí),遵循以下規(guī)則:C++規(guī)定,定義為基類(lèi)的指針,也能作指向派生類(lèi)的指針使用,并可以用這個(gè)指向派生類(lèi)對(duì)象的指針訪問(wèn)繼承來(lái)的基類(lèi)成員;但不能用它訪問(wèn)派生類(lèi)的成員。]
使用虛函數(shù)實(shí)現(xiàn)運(yùn)行時(shí)的多態(tài)性的關(guān)鍵在于:必須通過(guò)基類(lèi)指針訪問(wèn)這些函數(shù)。
一旦一個(gè)函數(shù)定義為虛函數(shù),無(wú)論它傳下去多少層,一直保持為虛函數(shù)。
把虛函數(shù)的再定義稱(chēng)為過(guò)載(overriding)而不叫重載(overloading)。
純虛函數(shù):是定義在基類(lèi)中的一種只給出函數(shù)原型,而沒(méi)有任何與該基類(lèi)有關(guān)的定義的函數(shù)。純虛函數(shù)使得任何派生類(lèi)都必須定義自己的函數(shù)版本。否則編譯報(bào)錯(cuò)。純虛函數(shù)定義的一般形式:
virtual type func_name(args)=0;
- 含有純虛函數(shù)的基類(lèi)稱(chēng)為抽象基類(lèi)。抽象基類(lèi)又一個(gè)重要特性:抽象類(lèi)不能建立對(duì)象。但是抽象基類(lèi)可以有指向自己的指針,以支持運(yùn)行時(shí)的多態(tài)性。
虛函數(shù)示例代碼
#include"test.h"
#include<iostream>
using namespace std;
class Base{
public:
void printf()
{
cout << "Base printf()" << endl;
}
virtual void func()
{
cout << "Base func()" << endl;
}
};
class Derived:public Base{
public:
void printf()
{
cout << "Derived printf()" << endl;
}
virtual void func()
{
cout << "Derived func()" << endl;
}
};
示例講解
在以上示例代碼中,我們聲明了一個(gè)父類(lèi) Base,和它的一個(gè)派生類(lèi) Derive,其中 printf() 實(shí)例方法是非虛函數(shù),而func()方法被聲明為了虛函數(shù)。并且在子類(lèi)中我們重新實(shí)現(xiàn)了printf() 和 func()方法。下面我們分別構(gòu)造出一個(gè) Derive 實(shí)例和Base 實(shí)例,分別用示例對(duì)象訪問(wèn)各func()和printf()方法。然后構(gòu)造新的Derived實(shí)例,并分別將其地址賦給 Base 指針和 Derived 指針,然后分別輸出訪問(wèn)func()和printf()方法的結(jié)果:
int main()
{
Base baseObj = Base();
baseObj.func();
baseObj.printf();
Derived derivedObj = Derived();
derivedObj.func();
derivedObj.printf();
Derived* pDerivedObj = new Derived();
Base* pBaseObj = pDerivedObj;
pDerivedObj->func();
pBaseObj->func();
pDerivedObj->printf();
pBaseObj->printf();
delete pDerivedObj;
return 0;
}
運(yùn)行結(jié)果
Terminal output result:
Base func()
Base printf()
Derived func()
Derived printf()
Derived func()
Derived func()
Derived printf()
Base printf()
結(jié)果描述
Base和Derived實(shí)例分別訪問(wèn)func()和printf()方法。運(yùn)行結(jié)果為各自對(duì)應(yīng)的func()和printf()方法輸出。
pDerivedObj 和 pBaseObj指針?lè)謩e指向了Derived實(shí)例的地址,對(duì)于 pDerivedObj 指針的操作表現(xiàn)出來(lái)它本身的方法輸出,然而當(dāng)我們把相同對(duì)象的地址賦給 pBaseObj 指針時(shí),可以發(fā)現(xiàn)它的非虛函數(shù)printf()竟然表現(xiàn)出了父類(lèi)的行為,并沒(méi)有被重寫(xiě)的樣子。那到底是什么原因造成了這樣的結(jié)果呢?我們繼續(xù)往下看虛函數(shù)表的介紹。
虛函數(shù)表以及內(nèi)存布局
虛函數(shù)(Virtual Function)是通過(guò)一張?zhí)摵瘮?shù)表(Virtual Table)來(lái)實(shí)現(xiàn)的。簡(jiǎn)稱(chēng)為V-Table。在這個(gè)表中,主是要一個(gè)類(lèi)的虛函數(shù)的地址表,這張表解決了繼承、覆蓋的問(wèn)題,保證其容真實(shí)反應(yīng)實(shí)際的函數(shù)。這樣,在有虛函數(shù)的類(lèi)的實(shí)例中這個(gè)表被分配在了這個(gè)實(shí)例的內(nèi)存中,所以,當(dāng)我們用父類(lèi)的指針來(lái)操作一個(gè)子類(lèi)的時(shí)候,這張?zhí)摵瘮?shù)表就顯得由為重要了,它就像一個(gè)地圖一樣,指明了實(shí)際所應(yīng)該調(diào)用的函數(shù)。
這里我們著重看一下這張?zhí)摵瘮?shù)表。C++的編譯器應(yīng)該是保證虛函數(shù)表的指針存在于對(duì)象實(shí)例中最前面的位置(這是為了保證取到虛函數(shù)表的有最高的性能——如果有多層繼承或是多重繼承的情況下)。 這意味著我們通過(guò)對(duì)象實(shí)例的地址得到這張?zhí)摵瘮?shù)表,然后就可以遍歷其中函數(shù)指針,并調(diào)用相應(yīng)的函數(shù)。
示例代碼(一下示例代碼編譯環(huán)境是X86并且采用4byte對(duì)齊)
非虛函數(shù)類(lèi)
class Base1
{
int a;
char c;
public:
void CommonFunction() {};
};
內(nèi)存布局情況
class Base1 size(8):
+---
0 | a
4 | c
| <alignment member> (size=3)
+---
+---
博主未來(lái)為了讓同學(xué)們注意一下在類(lèi)內(nèi)存布局中常見(jiàn)的字節(jié)對(duì)齊問(wèn)題,就專(zhuān)門(mén)在Base1類(lèi)中添加了char c變量。可以很清晰的看出在內(nèi)存中a和c成員變量依據(jù)聲明的順序進(jìn)行排列(類(lèi)內(nèi)偏移為0開(kāi)始)并且有3字節(jié)用于對(duì)齊,成員函數(shù)不占內(nèi)存空間。
單繼承派生類(lèi)不含非虛函數(shù)
class DerivedClass : public Base1
{
int c;
public:
void DerivedCommonFunction() {};
};
內(nèi)存布局情況
class DerivedClass size(12):
+---
0 | +--- (base class Base1)
0 | | a
4 | | c
| | <alignment member> (size=3)
| +---
8 | c
+---
可以看到子類(lèi)DerivedClass繼承了父類(lèi)Base1的成員變量,在內(nèi)存排布上,先是排布了父類(lèi)的成員變量,接著排布子類(lèi)的成員變量,同樣,成員函數(shù)不占字節(jié)。
存在虛函數(shù)類(lèi)
class Base1
{
int a;
char c;
public:
void CommonFunction() {};
void virtual VirtualFunction() {};
};
內(nèi)存分布情況
class Base1 size(12):
+---
0 | {vfptr}
4 | a
8 | c
| <alignment member> (size=3)
+---
Base1::$vftable@:
| &Base1_meta
| 0
0 | &Base1::VirtualFunction
這個(gè)內(nèi)存結(jié)構(gòu)圖分成了兩個(gè)部分,上面是內(nèi)存分布,下面是虛表,我們逐個(gè)看一下。從上圖可以看出虛表指針?lè)旁诹藘?nèi)存的開(kāi)始處(0地址偏移),然后再是成員變量;下面生成了虛表,緊跟在&Base1_meta后面的0表示,這張?zhí)摫韺?duì)應(yīng)的虛指針在內(nèi)存中的分布,下面列出了虛函數(shù),左側(cè)的0是這個(gè)虛函數(shù)的序號(hào),因?yàn)椴┲髦粚?xiě)了一個(gè)虛函數(shù),所以只有一項(xiàng),如果有多個(gè)虛函數(shù),會(huì)有序號(hào)為1,為2的虛函數(shù)列出來(lái)。
通過(guò)上面這個(gè)例子有同學(xué)就問(wèn)了虛表指針以及虛表是什么時(shí)候創(chuàng)建的呢? 構(gòu)造函數(shù)創(chuàng)建的時(shí)候即類(lèi)對(duì)象實(shí)例化的時(shí)候就創(chuàng)建的。那么如何利用虛表指針與虛表來(lái)實(shí)現(xiàn)多態(tài)的呢? 當(dāng)創(chuàng)建一個(gè)含有虛函數(shù)的父類(lèi)的對(duì)象時(shí),編譯器在對(duì)象構(gòu)造時(shí)將虛表指針指向父類(lèi)的虛函數(shù);同樣,當(dāng)創(chuàng)建子類(lèi)的對(duì)象時(shí),編譯器在構(gòu)造函數(shù)里將虛表指針(子類(lèi)只有一個(gè)虛表指針,它來(lái)自父類(lèi))指向子類(lèi)的虛表(這個(gè)虛表里面的虛函數(shù)入口地址是子類(lèi)的)從而可以實(shí)現(xiàn)多態(tài)。
單繼承派生類(lèi)中也有虛函數(shù)并且存在覆蓋繼承
class DerivedClass : public Base1
{
int d;
public:
void DerivedCommonFunction() {};
void virtual VirtualFunction() {};
};
內(nèi)存分布情況
class DerivedClass size(16):
+---
0 | +--- (base class Base1)
0 | | {vfptr}
4 | | a
8 | | c
| | <alignment member> (size=3)
| +---
12 | d
+---
DerivedClass::$vftable@:
| &DerivedClass_meta
| 0
0 | &DerivedClass::VirtualFunction
上半部是內(nèi)存分布,可以看到,虛表指針被繼承了,且仍位于內(nèi)存排布的起始處,下面是父類(lèi)的成員變量a和c,最后是子類(lèi)的成員變量d,注意虛表指針只有一個(gè),子類(lèi)并沒(méi)有再生成虛表指針了;下半部的虛表情況與父類(lèi)是一樣的由于子類(lèi)將父類(lèi)的虛函數(shù)方法重寫(xiě)了即產(chǎn)生的虛表序號(hào)只有一個(gè)。
單繼承派生類(lèi)中也有虛函數(shù)并且不存在覆蓋繼承
class Base1
{
int a;
char c;
public:
void CommonFunction() {};
void virtual VirtualFunction() {};
};
class DerivedClass : public Base1
{
int d;
public:
void DerivedCommonFunction() {};
void virtual VirtualFunction1() {};
};
內(nèi)存布局情況
class DerivedClass size(16):
+---
0 | +--- (base class Base1)
0 | | {vfptr}
4 | | a
8 | | c
| | <alignment member> (size=3)
| +---
12 | d
+---
DerivedClass::$vftable@:
| &DerivedClass_meta
| 0
0 | &Base1::VirtualFunction
1 | &DerivedClass::VirtualFunction1
此種情況內(nèi)存分布中上半部分也只有一個(gè)虛表指針變量?jī)?nèi)存分布依次排列,但是下方虛表的內(nèi)容變化了,虛表的0號(hào)是父類(lèi)的VirtualFunction,而1號(hào)放的是子類(lèi)的VirtualFunction2。也就是說(shuō),如果定義了DerivedClass的對(duì)象,那么在構(gòu)造時(shí),虛表指針就會(huì)指向這個(gè)虛表,以后如果調(diào)用的是VirtualFunction,那么會(huì)從父類(lèi)中尋找對(duì)應(yīng)的虛函數(shù),如果調(diào)用的是VirtualFunction1,那么會(huì)從子類(lèi)中尋找對(duì)應(yīng)的虛函數(shù)。
單繼承派生類(lèi)中即存在覆蓋虛函數(shù)也存在非覆蓋虛函數(shù)繼承
class Base1
{
int a;
char c;
public:
void CommonFunction() {};
void virtual VirtualFunction() {};
};
class DerivedClass : public Base1
{
int c;
public:
void DerivedCommonFunction() {};
void virtual VirtualFunction() {};
void virtual VirtualFunction1() {};
};
內(nèi)存布局情況
class DerivedClass size(16):
+---
0 | +--- (base class Base1)
0 | | {vfptr}
4 | | a
8 | | c
| | <alignment member> (size=3)
| +---
12 | c
+---
DerivedClass::$vftable@:
| &DerivedClass_meta
| 0
0 | &DerivedClass::VirtualFunction
1 | &DerivedClass::VirtualFunction1
根據(jù)上面的內(nèi)存布局情況,我們既重寫(xiě)了父類(lèi)的虛函數(shù),也有新添的虛函數(shù),最終虛函數(shù)表0號(hào)和1號(hào)都是子類(lèi)對(duì)應(yīng)的虛函數(shù)地址。
多繼承派生類(lèi)中存在覆蓋虛函數(shù)繼承
class Base1
{
int a;
char c;
public:
void CommonFunction() {};
void virtual VirtualFunction() {};
};
class DerivedClass1 : public Base1
{
int b;
public:
void DerivedCommonFunction() {};
void virtual VirtualFunction() {};
};
class DerivedClass2 : public Base1
{
int d;
public:
void DerivedCommonFunction() {};
void virtual VirtualFunction() {};
};
class DerivedDerivedClass : public DerivedClass1, public DerivedClass2
{
int e;
public:
void DerivedDerivedCommonFunction() {};
void virtual VirtualFunction() {};
};
內(nèi)存布局
class Base1 size(12):
+---
0 | {vfptr}
4 | a
8 | c
| <alignment member> (size=3)
+---
Base1::$vftable@:
| &Base1_meta
| 0
0 | &Base1::VirtualFunction
class DerivedClass1 size(16):
+---
0 | +--- (base class Base1)
0 | | {vfptr}
4 | | a
8 | | c
| | <alignment member> (size=3)
| +---
12 | b
+---
DerivedClass1::$vftable@:
| &DerivedClass1_meta
| 0
0 | &DerivedClass1::VirtualFunction
DerivedClass1::VirtualFunction this adjustor: 0
class DerivedClass2 size(16):
+---
0 | +--- (base class Base1)
0 | | {vfptr}
4 | | a
8 | | c
| | <alignment member> (size=3)
| +---
12 | d
+---
DerivedClass2::$vftable@:
| &DerivedClass2_meta
| 0
0 | &DerivedClass2::VirtualFunction
DerivedClass2::VirtualFunction this adjustor: 0
class DerivedDerivedClass size(36):
+---
0 | +--- (base class DerivedClass1)
0 | | +--- (base class Base1)
0 | | | {vfptr}
4 | | | a
8 | | | c
| | | <alignment member> (size=3)
| | +---
12 | | b
| +---
16 | +--- (base class DerivedClass2)
16 | | +--- (base class Base1)
16 | | | {vfptr}
20 | | | a
24 | | | c
| | | <alignment member> (size=3)
| | +---
28 | | d
| +---
32 | e
+---
DerivedDerivedClass::$vftable@DerivedClass1@:
| &DerivedDerivedClass_meta
| 0
0 | &DerivedDerivedClass::VirtualFunction
DerivedDerivedClass::$vftable@DerivedClass2@:
| -16
0 | &thunk: this-=16; goto DerivedDerivedClass::VirtualFunction
根據(jù)上面的內(nèi)存分布情況,此多繼承覆蓋情況,我分別把每個(gè)類(lèi)的內(nèi)存分布都打了出來(lái),下面我們重點(diǎn)看看這個(gè)類(lèi)DerivedDerivedClass,由外向內(nèi)看,它并列地排布著繼承而來(lái)的兩個(gè)父類(lèi)DerivedClass1與DerivedClass2,還有自身的成員變量e。DerivedClass1包含了它的成員變量b,以及Base1,Base1有一個(gè)0地址偏移的虛表指針,然后是成員變量a和c;DerivedClass2的內(nèi)存排布類(lèi)似于DerivedClass1,注意到DerivedClass2里面竟然也有一份Base1。
我們?cè)賮?lái)看看虛表繼承情況,我們看到了有兩份虛表了,分別針對(duì)DerivedClass1與DerivedClass2,在&DerivedDericedClass_meta下方的數(shù)字是首地址偏移量0也是DerivedClass1中的{vfptr}虛函數(shù)指針在DerivedDerivedClass的內(nèi)存偏移,靠下面的虛表的那個(gè)-16表示指向這個(gè)虛表的虛指針的內(nèi)存偏移,這正是DerivedClass2中的{vfptr}在DerivedDerivedClass的內(nèi)存偏移。
DerivedDerivedClass()的虛表的VirtualFunction()指針
[站外圖片上傳中...(image-a847ce-1565342394654)]
多繼承派生類(lèi)中不存在覆蓋虛函數(shù)繼承
class Base1
{
int a;
char c;
public:
void CommonFunction() {};
void virtual VirtualFunction() {};
};
class DerivedClass1 : public Base1
{
int b;
public:
void DerivedCommonFunction() {};
void virtual VirtualFunction1() {};
};
class DerivedClass2 : public Base1
{
int d;
public:
void DerivedCommonFunction() {};
void virtual VirtualFunction2() {};
};
class DerivedDerivedClass : public DerivedClass1, public DerivedClass2
{
int e;
public:
void DerivedDerivedCommonFunction() {};
void virtual VirtualFunction3() {};
};
內(nèi)存布局情況
class Base1 size(12):
+---
0 | {vfptr}
4 | a
8 | c
| <alignment member> (size=3)
+---
Base1::$vftable@:
| &Base1_meta
| 0
0 | &Base1::VirtualFunction
class DerivedClass1 size(16):
+---
0 | +--- (base class Base1)
0 | | {vfptr}
4 | | a
8 | | c
| | <alignment member> (size=3)
| +---
12 | b
+---
DerivedClass1::$vftable@:
| &DerivedClass1_meta
| 0
0 | &Base1::VirtualFunction
1 | &DerivedClass1::VirtualFunction1
DerivedClass1::VirtualFunction1 this adjustor: 0
class DerivedClass2 size(16):
+---
0 | +--- (base class Base1)
0 | | {vfptr}
4 | | a
8 | | c
| | <alignment member> (size=3)
| +---
12 | d
+---
DerivedClass2::$vftable@:
| &DerivedClass2_meta
| 0
0 | &Base1::VirtualFunction
1 | &DerivedClass2::VirtualFunction2
DerivedClass2::VirtualFunction2 this adjustor: 0
class DerivedDerivedClass size(36):
+---
0 | +--- (base class DerivedClass1)
0 | | +--- (base class Base1)
0 | | | {vfptr}
4 | | | a
8 | | | c
| | | <alignment member> (size=3)
| | +---
12 | | b
| +---
16 | +--- (base class DerivedClass2)
16 | | +--- (base class Base1)
16 | | | {vfptr}
20 | | | a
24 | | | c
| | | <alignment member> (size=3)
| | +---
28 | | d
| +---
32 | e
+---
DerivedDerivedClass::$vftable@DerivedClass1@:
| &DerivedDerivedClass_meta
| 0
0 | &Base1::VirtualFunction
1 | &DerivedClass1::VirtualFunction1
2 | &DerivedDerivedClass::VirtualFunction3
DerivedDerivedClass::$vftable@DerivedClass2@:
| -16
0 | &Base1::VirtualFunction
1 | &DerivedClass2::VirtualFunction2
此種情況的內(nèi)存分布和覆蓋多繼承一樣,唯一注意的就是在多繼承中成員虛函數(shù)地址會(huì)保存到第一個(gè)繼承父類(lèi)的虛函數(shù)表。
多繼承之虛繼承派生類(lèi)中存在覆蓋虛函數(shù)繼承
class Base1
{
int a;
char c;
public:
void CommonFunction() {};
void virtual VirtualFunction() {};
};
class DerivedClass1 : virtual public Base1
{
int b;
public:
void DerivedCommonFunction() {};
void virtual VirtualFunction1() {};
};
class DerivedClass2 : virtual public Base1
{
int d;
public:
void DerivedCommonFunction() {};
void virtual VirtualFunction2() {};
};
class DerivedDerivedClass : public DerivedClass1, public DerivedClass2
{
int e;
public:
void DerivedDerivedCommonFunction() {};
void virtual VirtualFunction3() {};
};
內(nèi)存分布情況
class Base1 size(12):
+---
0 | {vfptr}
4 | a
8 | c
| <alignment member> (size=3)
+---
Base1::$vftable@:
| &Base1_meta
| 0
0 | &Base1::VirtualFunction
class DerivedClass1 size(24):
+---
0 | {vfptr}
4 | {vbptr}
8 | b
+---
+--- (virtual base Base1)
12 | {vfptr}
16 | a
20 | c
| <alignment member> (size=3)
+---
DerivedClass1::$vftable@DerivedClass1@:
| &DerivedClass1_meta
| 0
0 | &DerivedClass1::VirtualFunction1
DerivedClass1::$vbtable@:
0 | -4
1 | 8 (DerivedClass1d(DerivedClass1+4)Base1)
DerivedClass1::$vftable@Base1@:
| -12
0 | &Base1::VirtualFunction
DerivedClass1::VirtualFunction1 this adjustor: 0
vbi: class offset o.vbptr o.vbte fVtorDisp
Base1 12 4 4 0
class DerivedClass2 size(24):
+---
0 | {vfptr}
4 | {vbptr}
8 | d
+---
+--- (virtual base Base1)
12 | {vfptr}
16 | a
20 | c
| <alignment member> (size=3)
+---
DerivedClass2::$vftable@DerivedClass2@:
| &DerivedClass2_meta
| 0
0 | &DerivedClass2::VirtualFunction2
DerivedClass2::$vbtable@:
0 | -4
1 | 8 (DerivedClass2d(DerivedClass2+4)Base1)
DerivedClass2::$vftable@Base1@:
| -12
0 | &Base1::VirtualFunction
DerivedClass2::VirtualFunction2 this adjustor: 0
vbi: class offset o.vbptr o.vbte fVtorDisp
Base1 12 4 4 0
class DerivedDerivedClass size(40):
+---
0 | +--- (base class DerivedClass1)
0 | | {vfptr}
4 | | {vbptr}
8 | | b
| +---
12 | +--- (base class DerivedClass2)
12 | | {vfptr}
16 | | {vbptr}
20 | | d
| +---
24 | e
+---
+--- (virtual base Base1)
28 | {vfptr}
32 | a
36 | c
| <alignment member> (size=3)
+---
DerivedDerivedClass::$vftable@DerivedClass1@:
| &DerivedDerivedClass_meta
| 0
0 | &DerivedClass1::VirtualFunction1
1 | &DerivedDerivedClass::VirtualFunction3
DerivedDerivedClass::$vftable@DerivedClass2@:
| -12
0 | &DerivedClass2::VirtualFunction2
DerivedDerivedClass::$vbtable@DerivedClass1@:
0 | -4
1 | 24 (DerivedDerivedClassd(DerivedClass1+4)Base1)
DerivedDerivedClass::$vbtable@DerivedClass2@:
0 | -4
1 | 12 (DerivedDerivedClassd(DerivedClass2+4)Base1)
DerivedDerivedClass::$vftable@Base1@:
| -28
0 | &Base1::VirtualFunction
上面虛繼承的內(nèi)存分布不做過(guò)多的敘述,下來(lái)總結(jié)一下:
虛繼承的作用是減少了對(duì)基類(lèi)的重復(fù)(在一般多繼承中會(huì)造成二義性編譯時(shí)出錯(cuò),虛繼承可以消除二義性),但是代價(jià)是增加了虛表指針的負(fù)擔(dān)(更多的虛表指針)。根據(jù)以上示例當(dāng)基類(lèi)有虛函數(shù)時(shí):
1 每個(gè)類(lèi)都有虛指針和虛表;
2 如果不是虛繼承,那么子類(lèi)將父類(lèi)的虛指針繼承下來(lái),并指向自身的虛表(發(fā)生在對(duì)象構(gòu)造時(shí))。有多少個(gè)虛函數(shù),虛表里面的項(xiàng)就會(huì)有多少。多重繼承時(shí),可能存在多個(gè)的基類(lèi)虛表與虛指針;
3 如果是虛繼承,那么子類(lèi)會(huì)有兩份虛指針,一份指向自己的虛表,另一份指向虛基表,多重繼承時(shí)虛基表與虛基表指針有且只有一份。
博客著作權(quán)歸本作者所有,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處。