概述
虛函式表是C++實作多型的一種方式,
問題:
- 什么情況下C++會使用虛指標和虛函式表?
- 如果子類不新增任何虛函式,也不重寫父類的虛方法,會和父類共用一張虛函式表么?
- 父類的建構式為什么不能正確的呼叫虛函式?
C++虛函式表指標和虛函式表
創建一個Base類
class Base
{
public:
int a;
int b;
};
查看Base記憶體布局
1>class Base size(8):
1> +---
1> 0 | a
1> 4 | b
1> +---
為Base類添加一個虛函式
class Base
{
public:
int a;
int b;
virtual void BaseFunc1()
{
std::cout << "Call BaseFunc1 From Base" << std::endl;
};
};
此時再查看Base類的記憶體布局
1>class Base size(12):
1> +---
1> 0 | {vfptr}
1> 4 | a
1> 8 | b
1> +---
1>Base::$vftable@:
1> | &Base_meta
1> | 0
1> 0 | &Base::BaseFunc1
1>Base::BaseFunc1 this adjustor: 0
Base類含有虛函式時,.rodata只讀資料區會生成一個虛函式表,Base類會生成一個指向該虛函式表的指標成員變數,虛函式表存放.text代碼區函式的地址,
再為Base添加一個虛函式
class Base
{
public:
int a;
int b;
virtual void BaseFunc1()
{
std::cout << "Call BaseFunc1 From Base" << std::endl;
};
virtual void BaseFunc2()
{
std::cout << "Call BaseFunc2 From Base" << std::endl;
}
};
查看Base類的記憶體分布
1>class Base size(12):
1> +---
1> 0 | {vfptr}
1> 4 | a
1> 8 | b
1> +---
1>Base::$vftable@:
1> | &Base_meta
1> | 0
1> 0 | &Base::BaseFunc1
1> 1 | &Base::BaseFunc2
1>Base::BaseFunc1 this adjustor: 0
1>Base::BaseFunc2 this adjustor: 0
Base類的虛函式表增加了一個新函式地址,
C++ 虛函式表和多型
為Base創建一個派生類Devire
class Derive : public Base
{
//
};
查看Derive類的記憶體分布
1>class Derive size(12):
1> +---
1> 0 | +--- (base class Base)
1> 0 | | {vfptr}
1> 4 | | a
1> 8 | | b
1> | +---
1> +---
1>Derive::$vftable@:
1> | &Derive_meta
1> | 0
1> 0 | &Base::BaseFunc1
1> 1 | &Base::BaseFunc2
虛函式表的內容和父類Base一樣
查看Base和Derive的虛函式表地址

Base和Derive并非公用一張虛函式表,
Derive重寫父類Base的方法
class Derive : public Base
{
public:
virtual void BaseFunc1() override
{
std::cout << "Call BaseFunc1 From Derive" << std::endl;
}
};
查看Derive類的記憶體分布
1>class Derive size(12):
1> +---
1> 0 | +--- (base class Base)
1> 0 | | {vfptr}
1> 4 | | a
1> 8 | | b
1> | +---
1> +---
1>Derive::$vftable@:
1> | &Derive_meta
1> | 0
1> 0 | &Derive::BaseFunc1
1> 1 | &Base::BaseFunc2
1>Derive::BaseFunc1 this adjustor: 0
此時虛函式表的0元素被替換成了Derive::BaseFunc1的地址,
為Derive添加一個新的虛函式
class Derive : public Base
{
public:
virtual void BaseFunc1() override
{
std::cout << "Call BaseFunc1 From Derive" << std::endl;
}
virtual void DeriveFunc1()
{
std::cout << "Call DeriveFunc1" << std::endl;
}
};
再查看Derive類的記憶體分布
1>class Derive size(12):
1> +---
1> 0 | +--- (base class Base)
1> 0 | | {vfptr}
1> 4 | | a
1> 8 | | b
1> | +---
1> +---
1>Derive::$vftable@:
1> | &Derive_meta
1> | 0
1> 0 | &Derive::BaseFunc1
1> 1 | &Base::BaseFunc2
1> 2 | &Derive::DeriveFunc1
1>Derive::BaseFunc1 this adjustor: 0
1>Derive::DeriveFunc1 this adjustor: 0
Derive的虛函式表添加了一個新的函式地址,
讓父類Base在建構式中呼叫虛函式BaseFunc1,
class Base
{
public:
Base()
{
BaseFunc1();
}
int a;
int b;
virtual void BaseFunc1()
{
std::cout << "Call BaseFunc1 From Base" << std::endl;
};
virtual void BaseFunc2()
{
std::cout << "Call BaseFunc2 From Base" << std::endl;
}
};
class Derive : public Base
{
public:
virtual void BaseFunc1() override
{
std::cout << "Call BaseFunc1 From Derive" << std::endl;
}
virtual void DeriveFunc1()
{
std::cout << "Call DeriveFunc1" << std::endl;
}
};
int main()
{
Derive d;
return 0;
}
輸出
Call BaseFunc1 From Base
虛函式的呼叫是錯誤的,
查看Derive和Base的建構式的匯編代碼
Base 建構式匯編代碼
...
00641F4D mov dword ptr [eax],offset Base::`vftable' (0649B34h)
{
BaseFunc1();
00641F53 mov ecx,dword ptr [this]
00641F56 call Base::BaseFunc1 (0641488h)
}
00641F5B mov eax,dword ptr [this]
...
Derive 建構式的匯編代碼
...
0064220A mov ecx,dword ptr [this]
0064220D call Base::Base (06412B2h)
{
00642212 mov eax,dword ptr [this]
00642215 mov dword ptr [eax],offset Derive::`vftable' (0649B40h)
//
}
...
觀察匯編代碼可知,構造Devire類的物件時,當呼叫父類Base的建構式時,此時虛指標指向的虛函式表是父類Base的,只有呼叫Derive自己的建構式時,虛指標被賦值為Derive的虛函式表,所以父類的建構式不能正確的呼叫虛函式,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/495199.html
標籤:C++
