C++中的動(dòng)態(tài)分派在HotSpot?VM中的應(yīng)用小結(jié)
眾所周知,多態(tài)是面向?qū)ο缶幊陶Z言的重要特性,它允許基類的指針或引用指向派生類的對(duì)象,而在具體訪問時(shí)實(shí)現(xiàn)方法的動(dòng)態(tài)綁定。C++ 和 Java 作為當(dāng)前最為流行的兩種面向?qū)ο缶幊陶Z言,其內(nèi)部對(duì)于多態(tài)的支持對(duì)于單繼承的實(shí)現(xiàn)非常類似。
首先來體現(xiàn)一下C++的動(dòng)態(tài)分派,如下:
class Base1{
public:
int base1_var1;
int base1_var2;
void func(){}
};C++中有函數(shù)的動(dòng)態(tài)分派,就類似于Java中方法的多態(tài)。而C++實(shí)現(xiàn)動(dòng)態(tài)分派主要就是通過虛函數(shù)來完成的,非虛函數(shù)在編譯時(shí)就已經(jīng)確定調(diào)用目標(biāo)。C++中的虛函數(shù)通過關(guān)鍵字virtual來聲明,如上函數(shù)func()沒有virtual關(guān)鍵字,所以是非虛函數(shù)。
查看內(nèi)存布局,如下:
1> class Base1 size(8): 1> +--- 1> 0 | base1_var1 1> 4 | base1_var2 1> +---
非虛函數(shù)不會(huì)影響內(nèi)存布局?!?/p>
class Base1{
public:
int base1_var1;
int base1_var2;
virtual void base1_fun1() {}
};內(nèi)存布局如下:
1> class Base1 size(16):
1> +---
1> 0 | {vfptr}
1> 8 | base1_var1
1> 12 | base1_var2
1> +---在64位環(huán)境下,指針占用8字節(jié),而vfptr就是指向虛函數(shù)表(vtable)的指針,其類型為void**, 這說明它是一個(gè)void*指針。類似于在類Base1中定義了如下類似的偽代碼:
void* vtable[1] = { &Base1::base1_fun1 };
const void** vfptr = &vtable[0];這個(gè)非常類似于Java虛擬機(jī)中對(duì)Java方法動(dòng)態(tài)分派時(shí)的虛函數(shù)表(可參看深入剖析Java虛擬機(jī):源碼剖析與實(shí)例詳解》一書第6.3節(jié))。
虛函數(shù)表是屬于類的,而不是屬于某個(gè)具體的對(duì)象,一個(gè)類只需要一個(gè)虛函數(shù)表即可。同一個(gè)類的所有對(duì)象都使用同一個(gè)虛函數(shù)表。為了指定對(duì)象的虛函數(shù)表,對(duì)象內(nèi)部包含一個(gè)虛函數(shù)表的指針,來指向自己所使用的虛函數(shù)表。?為了讓每個(gè)包含虛函數(shù)表的類的對(duì)象都擁有一個(gè)虛函數(shù)表指針,編譯器在類中添加了一個(gè)指針,用來指向虛函數(shù)表。這樣,當(dāng)類的對(duì)象在創(chuàng)建時(shí)便擁有了這個(gè)指針,且這個(gè)指針的值會(huì)自動(dòng)被設(shè)置為指向類的虛函數(shù)表。
從如上的例子我們應(yīng)該能夠得到如下一些結(jié)論:
- C++的動(dòng)態(tài)分派需要明確用virtual關(guān)鍵字指明,而Java中的方法默認(rèn)就是動(dòng)態(tài)分派,這也體現(xiàn)出兩種語言設(shè)計(jì)理念的不同,C++不想讓用戶為用不到的功能付出代價(jià),Java更看重開發(fā)效率,將一些復(fù)雜的底層控制全權(quán)托管給虛擬機(jī);
- C++中只要有虛函數(shù),就會(huì)在類中有一個(gè)虛函數(shù)表,而且類的每個(gè)實(shí)例都會(huì)多出一個(gè)指向虛函數(shù)表的指針。
對(duì)于第2點(diǎn)來說,HotSpot VM的設(shè)計(jì)者充分考慮了這個(gè)情況,所以在一些類的設(shè)計(jì)上能不用虛函數(shù)就絕對(duì)不用,例如oop繼承體系下的所有類都不會(huì)用虛函數(shù),因?yàn)橛幸粋€(gè)Java實(shí)例就會(huì)有一個(gè)oop,現(xiàn)在的應(yīng)用程序一般都有過千萬的實(shí)例,那我們可以算一下,每個(gè)實(shí)例要多出8個(gè)字節(jié)存儲(chǔ)指向虛表的指針,那要消耗掉多少內(nèi)存呢?!
下面我們來談?wù)劦?點(diǎn)提到的動(dòng)態(tài)分派。先來看一下單繼承情況下C++的動(dòng)態(tài)分派。
class Person{
. . .
public :
void init(){} // 非virtual方法
virtual void sing (){};
virtual void dance (){};
};
class Girl : public Person{
. . .
public :
virtual void sing(){};
virtual void speak(){};
};動(dòng)態(tài)分派的過程大概如下圖所示。

只有加virtual關(guān)鍵字的虛函數(shù)才會(huì)存在于虛函數(shù)表中,也就是這些虛函數(shù)需要?jiǎng)討B(tài)分派。當(dāng)子類重寫了父類的方法時(shí),動(dòng)態(tài)分派能準(zhǔn)確根據(jù)接收者類型找到實(shí)際需要調(diào)用的函數(shù)。
在HotSpot VM中有非常多使用動(dòng)態(tài)分派的例子,如Klass繼承體系下的類有個(gè)oop_oop_iterate_v_m()函數(shù),在發(fā)生YGC時(shí),由于老年代也會(huì)有引用指向年輕代對(duì)象,所以必須通過卡表找到這些可能的對(duì)象,在找到這些對(duì)象后,這些對(duì)象的哪些區(qū)域是引用還要進(jìn)一步借助Klass來完成,如一般的對(duì)象會(huì)調(diào)用InstanceKlass中的oop_oop_iterate_v_m()函數(shù),而java.lang.Class對(duì)象會(huì)調(diào)用InstanceMirrorKlass類中的oop_oop_iterate_v_m()函數(shù)。
下面看一下Java的動(dòng)態(tài)分派。舉個(gè)例子如下:
class Person{
void sing (){}
void dance (){}
}
class Girl extends Person{
void sing(){}
void speak(){}
}了解過Java虛函數(shù)表的人可能知道,類中許多的方法都會(huì)放到虛函數(shù)表中,這就是我們前面說的,Java中的方法默認(rèn)都是動(dòng)態(tài)分派的。
動(dòng)態(tài)分派的過程大概如下圖所示。

從Object繼承下來5個(gè)方法,final、靜態(tài)方法和構(gòu)造方法不會(huì)進(jìn)入虛函數(shù)表中。final關(guān)鍵字與C++的virtual有著相反的作用。
JVM的方法調(diào)用指令有四個(gè),分別是 invokestatic,invokespecial,invokesvirtual 和 invokeinterface。前兩個(gè)是靜態(tài)綁定,后兩個(gè)是動(dòng)態(tài)綁定的,invokevirtual表示調(diào)用虛方法,也就是會(huì)查虛函數(shù)表進(jìn)行調(diào)用,而invokeinterface表示調(diào)用接口方法,會(huì)有個(gè)接口函數(shù)表,這里暫不介紹。
到此這篇關(guān)于C++中的動(dòng)態(tài)分派在HotSpot VM中的應(yīng)用小結(jié)的文章就介紹到這了,更多相關(guān)C++動(dòng)態(tài)分派HotSpot VM應(yīng)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中decltype關(guān)鍵字的實(shí)現(xiàn)
decltype是C++11引入的關(guān)鍵字,用于在編譯時(shí)精確推導(dǎo)表達(dá)式或變量的類型而不執(zhí)行表達(dá)式,本文就來詳細(xì)的介紹一下C++ decltype關(guān)鍵字的使用,感興趣的可以了解一下2026-02-02
C語言結(jié)構(gòu)體數(shù)組同時(shí)賦值的另類用法
今天小編就為大家分享一篇關(guān)于C語言結(jié)構(gòu)體數(shù)組同時(shí)賦值的另類用法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12
C語言 ffmpeg與sdl實(shí)現(xiàn)播放視頻同時(shí)同步時(shí)鐘詳解
使用ffmpeg和sdl實(shí)現(xiàn)播放視頻后,需要再實(shí)現(xiàn)時(shí)鐘同步才能正常的播放視頻,尤其是有音頻的情況,我們通常需要將視頻同步到音頻來確保音畫同步2022-09-09
c++將字符串轉(zhuǎn)數(shù)字的實(shí)例方法
在本篇文章里小編給大家整理的是關(guān)于c++將字符串轉(zhuǎn)數(shù)字的實(shí)例方法,有需要的朋友們可以參考下。2020-02-02
C++中獲取隨機(jī)數(shù)的常用方法小結(jié)
這篇文章主要為大家詳細(xì)介紹了C++中獲取隨機(jī)數(shù)的幾種常用方法,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解下2025-01-01

