最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++虛表&多態(tài)的實(shí)現(xiàn)原理分析

 更新時(shí)間:2025年09月09日 15:42:57   作者:愛上小公舉  
文章介紹了C++虛函數(shù)的實(shí)現(xiàn)機(jī)制,通過虛函數(shù)指針(vfptr)和虛函數(shù)表(vftable)實(shí)現(xiàn)多態(tài),對(duì)象包含vfptr指針,虛表存函數(shù)地址并以空指針結(jié)尾,繼承時(shí)虛表繼承并擴(kuò)展,多態(tài)需基類指針引用和虛函數(shù)重寫,利用虛表實(shí)現(xiàn)動(dòng)態(tài)綁定

C++虛表&多態(tài)實(shí)現(xiàn)原理

這里我們只看繼承中的多態(tài) ,本文程序調(diào)試在VS2017

先看一個(gè)小問題,下面的類A實(shí)例化出的對(duì)象占幾個(gè)字節(jié)呢?

#include<iostream>
using namespace std;
class A {
	int m_a;
public:
	void func(){
		cout << "類A的func" << endl;
	}
};
int main() {
        A a;
	cout << sizeof(a) << endl;
	system("pause");
	return 0;
}

答案是4字節(jié),這是因?yàn)?strong>成員函數(shù)存放在公共的代碼段, 所以只計(jì)算成員變量m_a所占字節(jié)的大小

調(diào)試一下來看, 

我們?nèi)绻麑⒊蓡T函數(shù)定義成虛函數(shù)又會(huì)如何呢?

我們來看:

#include<iostream>
using namespace std;
class A {
	int m_a;
public:
	virtual void func(){
		cout << "類A的func" << endl;
	}
	virtual int func1() {
		cout << "類A的func1" << endl;
		return 0;
	}
};
int main() {
	A a,b;
	cout << sizeof(A) << endl;
	system("pause");
	return 0;
}

可以看到結(jié)果是8字節(jié),emm.... 事出反常必有妖,我們調(diào)試一下,看看到底多出來了個(gè)什么東西

我們可以看到,在實(shí)例化出的對(duì)象a和b中多了一個(gè)_vfptr,它的類型時(shí)void**,是一個(gè)二級(jí)指針,指針在32位平臺(tái)中占4字節(jié),所以這里的結(jié)果是8(m_a的4字節(jié)+_vfptr的4字節(jié)),那么_vfptr到底是個(gè)什么東西? 

類中有了虛函數(shù)之后才有了_vfptr,它們之間到底有著什么關(guān)系?

其實(shí),_vfptr,其實(shí)就是虛函數(shù)指針 (virtual function pointer)

可以看到_vfptr 指向了一個(gè) vftable(virtual function table) 虛函數(shù)表(也叫虛表),虛表中元素是void*類型 ,第一個(gè)元素是指向了虛函數(shù)func(),第二個(gè)元素指向了fun1()

虛函數(shù)指針(_vfptr) 和 虛函數(shù)表(vftable)

虛函數(shù)指針和虛表是什么

通過上面的調(diào)試,我們已經(jīng)看到了,_vfptr是指向虛表的一個(gè)指針,那么我們也可以叫 _vfptr 為虛函數(shù)表指針

  • 當(dāng)一個(gè)類中有虛函數(shù)時(shí),編譯期間,就會(huì)為這個(gè)類分配一片連續(xù)的內(nèi)存 (這就是虛表vftable),來存放虛函數(shù)的地址,類中只保存著
  • 指向虛表的指針 (也就是虛函數(shù)表指針_vfptr)  ,(虛函數(shù)其實(shí)和普通函數(shù)一樣,存放在代碼段) ,當(dāng)這個(gè)類實(shí)例出對(duì)象時(shí),每個(gè)對(duì)象
  • 都會(huì)有一個(gè)虛函數(shù)表指針_vfptr    (VS中虛表內(nèi)存分配在代碼段)

虛表本質(zhì)上是一個(gè)在編譯時(shí)就已經(jīng)確定好了的void* 類型的指針數(shù)組 .

注意 :  虛函數(shù)表為了標(biāo)志結(jié)尾,會(huì)在虛表最后一個(gè)元素位置保存一個(gè)空指針.所以看到的虛表元素個(gè)數(shù)比實(shí)際虛函數(shù)個(gè)數(shù)多一個(gè)

C++中的虛函數(shù)的實(shí)現(xiàn)一般是通過虛函數(shù)表 (C++規(guī)范并沒有規(guī)定具體用哪種方法,但大部分的編譯器都用虛函數(shù)表的方法)  大多數(shù)編譯器(如本文用的VS)中虛函數(shù)表指針都在對(duì)象的最前面位置,意味著能通過對(duì)象的地址就能遍歷虛函數(shù)表(能夠在多層繼承或多重繼承中保持較高性能)

虛函數(shù)是為了繼承時(shí)的多態(tài)才有的概念,上面簡單了解了一下虛表,我們?cè)賮砜蠢^承關(guān)系中的虛表

繼承中的虛表

在有虛函數(shù)的類(有虛表的類)被繼承后,  虛表也會(huì)被拷貝給派生類. 注意,編譯器會(huì)給派生類新分配一片空間來拷貝基類的虛表,將這個(gè)虛表的指針給派生類, 而并不是沿用基類的虛表,在發(fā)生虛函數(shù)的重寫時(shí),重寫的是派生類為了拷貝基類虛表新創(chuàng)建的這虛表中的虛函數(shù)地址

虛表為所有這個(gè)類的對(duì)象所共享.  注意,是通過給每個(gè)對(duì)象一個(gè)虛表指針_vfptr共享到的虛表.

單繼承中的虛表

1. 單繼承未重寫虛函數(shù): 會(huì)繼承派生類的虛表,如果派生類中新增了虛函數(shù),則會(huì)加繼承的虛表后面

2. 單繼承重寫虛函數(shù): 繼承的虛表中被重寫的虛函數(shù)地址會(huì)在繼承虛表時(shí)被修改為派生類函數(shù)的地址(如下面例子中把A::func()修改成了B::func()的地址)(注意: 此時(shí)基類的虛表并沒有被修改,修改的是派生類自己的虛表)

所以, 重寫實(shí)際上就是在繼承基類虛表時(shí),把基類的虛函數(shù)地址修改為派生類虛函數(shù)的地址

舉個(gè)栗子

#include<iostream>
using namespace std;
class A {
	int m_a;
public:
	virtual void func(){
		cout << "類A的func" << endl;
	}
	virtual int func1() {
		cout << "類A的func1" << endl;
		return 0;
	}
};
class B :public A {
public:
	virtual void func() {
		cout << "類B的func" << endl;
	}
	virtual void func2() {
		cout << "類B的func2" << endl;
	}
};
int main() {
        A a1;
        A a2;
	B b;
	system("pause");
	return 0;
}

調(diào)試如下:      

可以看到對(duì)象b中的_vfptr所指向的虛表繼承了類A的虛表 ,但是地址卻和a1,a2的_vfptr不一樣,也印證了前面所說,新分配了一片空間來拷貝基類的虛表. 

還可以看到a1和a2的虛表地址相同,也印證了前面所說,類的虛表被所有對(duì)象所共享. 

但我們卻發(fā)現(xiàn),B中也有虛函數(shù),怎么沒有了,講道理這是不科學(xué)的,那么B類的虛函數(shù)的地址放到底哪去了呢?

思考一下,它肯定是存在的,要么另外建一張?zhí)摫矸爬锩?,要么放在繼承A的虛表里.

實(shí)際上,在單繼承中,派生類的虛函數(shù)地址會(huì)放在繼承來的基類的虛表后面,只是VS這里沒有顯示出來.

我們也可以看到圖中紅色圈出來的vftable[4],虛表中也已經(jīng)有了四個(gè)元素,既然VS不給力,只能自己想辦法,可以在監(jiān)視窗口,通過地址看到,B類中的虛函數(shù)指針指向的虛表,其實(shí)是這樣的,如下 

還可以看到,繼承的A中的虛函數(shù)func()被重寫之后,虛表中就放的是重寫后的B中的func()的地址 

多繼承中的虛表

  • 多繼承不重寫虛函數(shù): 繼承的多個(gè)基類中有多張?zhí)摫恚缮悤?huì)全部拷貝下來,成為派生類的多張?zhí)摫?,如果派生類有新的虛函?shù),會(huì)加在派生類拷貝的第一張?zhí)摫淼暮竺?拷貝的第一張?zhí)摫硎抢^承的第一個(gè)有虛函數(shù)(或虛表)的基類的)
  • 多繼承重寫虛函數(shù) : 規(guī)則與 不重寫虛函數(shù) 相同,但需要注意的是,如果多個(gè)基類中含有相同的虛函數(shù),例如func. 當(dāng)派生類重寫func這個(gè)虛函數(shù)后,所有含有這個(gè)函數(shù)的基類虛表都會(huì)被重寫 (改的是派生類自己拷貝的基類虛表,并不是基類自己的虛表)

舉個(gè)栗子

#include<iostream>
using namespace std;
class A {
	int m_a;
public:
	virtual void funcA() {
		cout << "類A的funcA" << endl;
	}
	virtual void func() {
		cout << "類A的func" << endl;
	}
};
class B {
public:
	virtual void funcB() {
		cout << "類B的funcB" << endl;
	}
	virtual void func() {
		cout << "類B的func" << endl;
	}
};
class C :public A,public B {
public:
	virtual void func() {
		cout << "類C的func" << endl;
	}
	virtual void funcC() {
		cout << "類C的funcC" << endl;
	}
};
int main() {
	C c;
	A a;
	system("pause");
	return 0;
}

調(diào)試如下 :

可以看到,派生類繼承了兩張?zhí)摫?,A::func()和B::func()的地址修改為了C:::func()的地址

我們?cè)俅瓮ㄟ^_vfptr的地址,在監(jiān)視窗口可以看到,派生類中新增的虛函數(shù),虛函數(shù)地址被加在了派生類拷貝基類的第一張?zhí)摫淼暮竺? 

多態(tài)的原理

我們回憶一下多態(tài)的兩個(gè)構(gòu)成條件

  • 1.通過指向派生類對(duì)象的基類的指針或引用調(diào)用虛函數(shù)
  • 2. 被調(diào)用的函數(shù)必須是被派生類重寫過的虛函數(shù)

簡單來說就是,利用了虛函數(shù)可以重寫的特性,當(dāng)一個(gè)有虛函數(shù)的基類有多個(gè)派生類時(shí),通過各個(gè)派生類對(duì)基類虛函數(shù)的不同重寫,實(shí)現(xiàn)通過指向派生類對(duì)象基類指針基類引用調(diào)用同一個(gè)虛函數(shù),去實(shí)現(xiàn)不同功能的特性. 抽象來說就是,為了完成某個(gè)行為,不同的對(duì)象去完成時(shí)會(huì)產(chǎn)生多種不同的狀態(tài)

總結(jié)

  • 一個(gè)有虛函數(shù)的類,它實(shí)例出的所有對(duì)象通過虛表指針vfptr共享類的虛表
  • 對(duì)象中存放的是虛函數(shù)(表)指針vfptr,不是虛表. vfptr是虛表的首地址,指向虛表
  • 虛表中存放的時(shí)虛函數(shù)地址,不是虛函數(shù),虛函數(shù)和普通函數(shù)一樣,存放在代碼段
  • 虛表是在編譯階段生成的,一般分配在在代碼段(常量區(qū)),例如VS中

派生類的虛表生成:

  • a.先將基類中的虛表內(nèi)容拷貝一份到派生類虛表中
  • b.如果派生類重寫了基類中某個(gè)虛函數(shù),用派生類自己的虛函數(shù)覆蓋虛表中基類的虛函數(shù)
  • c.派生類自己新增加的虛函數(shù)按其在派生類中的聲明次序增加到派生類虛表的最后。
  • d.如是多繼承,則派生類新增加的虛函數(shù)地址添加在派生類拷貝(繼承)的第一張?zhí)摫砗竺?/li>

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • C語言數(shù)據(jù)結(jié)構(gòu)的時(shí)間復(fù)雜度和空間復(fù)雜度

    C語言數(shù)據(jù)結(jié)構(gòu)的時(shí)間復(fù)雜度和空間復(fù)雜度

    算法在編寫成可執(zhí)行程序后,運(yùn)行時(shí)需要耗費(fèi)時(shí)間資源和空間(內(nèi)存)資源 。因此衡量一個(gè)算法的好壞,一般是從時(shí)間和空間兩個(gè)維度來衡量的,即時(shí)間復(fù)雜度和空間復(fù)雜度,感興趣的同學(xué)可以參考閱讀
    2023-04-04
  • 基于C語言char與unsigned char的區(qū)別介紹

    基于C語言char與unsigned char的區(qū)別介紹

    本篇文章小編為大家介紹,基于C語言char與unsigned char的區(qū)別介紹。需要的朋友參考下
    2013-04-04
  • C語言結(jié)構(gòu)體指針案例解析

    C語言結(jié)構(gòu)體指針案例解析

    這篇文章主要介紹了C語言結(jié)構(gòu)體指針案例解析,本文通過例子來解釋說明了C語言的結(jié)構(gòu)體概念和如何用指針去操作結(jié)構(gòu)體,文章標(biāo)明了詳細(xì)的代碼,需要的朋友可以參考下
    2021-07-07
  • C++標(biāo)準(zhǔn)庫實(shí)現(xiàn)WAV文件讀寫的操作

    C++標(biāo)準(zhǔn)庫實(shí)現(xiàn)WAV文件讀寫的操作

    本文將使用標(biāo)準(zhǔn)C++庫實(shí)現(xiàn)對(duì)數(shù)據(jù)為PCM格式的WAV文件的讀寫操作,只使用標(biāo)準(zhǔn)C++庫函數(shù),不依賴于其他的庫,對(duì)C++標(biāo)準(zhǔn)庫實(shí)現(xiàn)WAV文件讀寫相關(guān)知識(shí)感興趣的朋友一起看看吧
    2022-01-01
  • QT安裝MQTT庫的實(shí)現(xiàn)步驟

    QT安裝MQTT庫的實(shí)現(xiàn)步驟

    本文分別介紹了在Windows、Linux、ARM板卡下給QT安裝MQTT庫的步驟,具有一定的參考價(jià)值,感興趣的可以了解一下
    2026-01-01
  • C++日期類的實(shí)現(xiàn)日期計(jì)算器舉例詳解

    C++日期類的實(shí)現(xiàn)日期計(jì)算器舉例詳解

    這篇文章主要給大家介紹了關(guān)于C++日期類實(shí)現(xiàn)日期計(jì)算器的相關(guān)資料,我們要考慮日期的增加和減少,自增和自減,以及兩個(gè)日期類的比較,以及當(dāng)前日期類的日期顯示和用戶的輸入輸出,需要的朋友可以參考下
    2024-05-05
  • C++之多態(tài)(一個(gè)接口多種實(shí)現(xiàn)方式)

    C++之多態(tài)(一個(gè)接口多種實(shí)現(xiàn)方式)

    C++多態(tài)分靜態(tài)(重載)與動(dòng)態(tài)(虛函數(shù)),動(dòng)態(tài)依賴虛函數(shù)表和虛指針實(shí)現(xiàn)運(yùn)行時(shí)綁定,重寫需滿足函數(shù)簽名一致,純虛函數(shù)構(gòu)建抽象類,多態(tài)提升代碼靈活性與擴(kuò)展性,但需注意析構(gòu)函數(shù)虛化及虛函數(shù)表開銷
    2025-09-09
  • 詳解C++虛函數(shù)中多態(tài)性的實(shí)現(xiàn)原理

    詳解C++虛函數(shù)中多態(tài)性的實(shí)現(xiàn)原理

    C++是一種面向?qū)ο蟮木幊陶Z言,在C++中,虛函數(shù)是實(shí)現(xiàn)多態(tài)性的關(guān)鍵。本文就來探討一下C++虛函數(shù)中多態(tài)性的實(shí)現(xiàn)原理及其在面向?qū)ο缶幊讨械膽?yīng)用吧
    2023-05-05
  • C語言在輸入輸出時(shí)遇到的常見問題總結(jié)

    C語言在輸入輸出時(shí)遇到的常見問題總結(jié)

    大家在平時(shí)的做題中是否會(huì)遇到和我一樣的煩惱,題目的代碼已經(jīng)基本完成,但是在輸出時(shí)候,總是和題目給出的樣例輸出格式不同?,導(dǎo)致題目不能通過。為了解決這一煩惱,我總結(jié)了以下幾點(diǎn),需要的可以參考一下
    2022-09-09
  • C語言中const,volatile,restrict的用法總結(jié)

    C語言中const,volatile,restrict的用法總結(jié)

    以下是對(duì)C語言中const,volatile,restrict的用法進(jìn)行了詳細(xì)的總結(jié)介紹,需要的朋友可以過來參考下
    2013-10-10

最新評(píng)論

五台县| 嵊州市| 株洲市| 山丹县| 商丘市| 麦盖提县| 潢川县| 潜江市| 三江| 松滋市| 新蔡县| 南丹县| 丽江市| 潢川县| 吴江市| 静海县| 江口县| 栾川县| 唐山市| 犍为县| 冕宁县| 阳新县| 馆陶县| 宜兰市| 玛多县| 丹棱县| 犍为县| 洪泽县| 平昌县| 临猗县| 英德市| 桐城市| 天全县| 石林| 禄丰县| 阿拉善右旗| 伊宁市| 新龙县| 栾城县| 滁州市| 平江县|