C++學(xué)習(xí)之虛函數(shù)表與多態(tài)詳解
概述
C++的多態(tài)在不同環(huán)境下實現(xiàn)方式可能不一樣,虛函數(shù)表是C++實現(xiàn)多態(tài)的一種方式。
問題:
- 什么情況下C++會使用虛指針和虛函數(shù)表?
- 如果子類不新增任何虛函數(shù),也不重寫父類的虛方法,會和父類共用一張?zhí)摵瘮?shù)表么?
- 父類的構(gòu)造函數(shù)為什么不能正確的調(diào)用虛函數(shù)?
C++虛函數(shù)表指針和虛函數(shù)表
創(chuàng)建一個Base類
class Base { public: int a; int b; };
查看Base內(nèi)存布局
1>class Base size(8):
1> +---
1> 0 | a
1> 4 | b
1> +---
為Base類添加一個虛函數(shù)
class Base { public: int a; int b; virtual void BaseFunc1() { std::cout << "Call BaseFunc1 From Base" << std::endl; }; };
此時再查看Base類的內(nèi)存布局
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類含有虛函數(shù)時,.rodata只讀數(shù)據(jù)區(qū)會生成一個虛函數(shù)表,Base類會生成一個指向該虛函數(shù)表的指針成員變量。虛函數(shù)表存放.text代碼區(qū)函數(shù)的地址。
再為Base添加一個虛函數(shù)
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類的內(nèi)存分布
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類的虛函數(shù)表增加了一個新函數(shù)地址。
C++ 虛函數(shù)表和多態(tài)
為Base創(chuàng)建一個派生類Devire
class Derive : public Base { // };
查看Derive類的內(nèi)存分布
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
虛函數(shù)表的內(nèi)容和父類Base一樣
查看Base和Derive的虛函數(shù)表地址
Base和Derive并非公用一張?zhí)摵瘮?shù)表。
Derive重寫父類Base的方法
class Derive : public Base { public: virtual void BaseFunc1() override { std::cout << "Call BaseFunc1 From Derive" << std::endl; } };
查看Derive類的內(nèi)存分布
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
此時虛函數(shù)表的0元素被替換成了Derive::BaseFunc1的地址。
為Derive添加一個新的虛函數(shù)
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類的內(nèi)存分布
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的虛函數(shù)表添加了一個新的函數(shù)地址。
讓父類Base在構(gòu)造函數(shù)中調(diào)用虛函數(shù)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
虛函數(shù)的調(diào)用是錯誤的。
查看Derive和Base的構(gòu)造函數(shù)的匯編代碼
Base 構(gòu)造函數(shù)匯編代碼
...
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 構(gòu)造函數(shù)的匯編代碼
...
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)
//
}
...
觀察匯編代碼可知,構(gòu)造Devire類的對象時,當(dāng)調(diào)用父類Base的構(gòu)造函數(shù)時,此時虛指針指向的虛函數(shù)表是父類Base的,只有調(diào)用Derive自己的構(gòu)造函數(shù)時,虛指針被賦值為Derive的虛函數(shù)表,所以父類的構(gòu)造函數(shù)不能正確的調(diào)用虛函數(shù)。
以上就是C++學(xué)習(xí)之虛函數(shù)表與多態(tài)詳解的詳細(xì)內(nèi)容,更多關(guān)于C++虛函數(shù)表 多態(tài)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
string,CString,char*之間的轉(zhuǎn)化
下面是MFC/C++/C中字符類型CString, int, string, char*之間的轉(zhuǎn)換的說明與舉例,經(jīng)常用的東西,相信對于用C/C++的朋友,還是比較有用的2013-03-03C語言修煉之路一朝函數(shù)思習(xí)得?模塊思維世間生上篇
函數(shù)是一組一起執(zhí)行一個任務(wù)的語句。每個?C?程序都至少有一個函數(shù),即主函數(shù)?main()?,所有簡單的程序都可以定義其他額外的函數(shù)2022-03-03C++?Qt開發(fā)之關(guān)聯(lián)容器類使用方法詳解
當(dāng)我們談?wù)摼幊讨械臄?shù)據(jù)結(jié)構(gòu)時,順序容器是不可忽視的一個重要概念,Qt?中提供了豐富的容器類,用于方便地管理和操作數(shù)據(jù),本章我們將主要學(xué)習(xí)關(guān)聯(lián)容器,主要包括?QMap?,QSet和?QHash,感興趣的朋友跟著小編一起來學(xué)習(xí)吧2023-12-12