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