高性能網(wǎng)站建設(shè)進(jìn)階指南:web開發(fā)者性能優(yōu)化最佳實(shí)踐重慶網(wǎng)站優(yōu)化軟件
在 C++ 中,虛函數(shù)表(Virtual Function Table,簡稱 vtable)是一種用于實(shí)現(xiàn)多態(tài)性(Polymorphism)的機(jī)制。它是一種編譯器和鏈接器生成的數(shù)據(jù)結(jié)構(gòu),用于處理虛函數(shù)調(diào)用。
虛函數(shù)是在基類中聲明的,可以在派生類中被重寫(覆蓋)的函數(shù)。虛函數(shù)表是用于管理這些虛函數(shù)的數(shù)據(jù)結(jié)構(gòu)。每個(gè)包含虛函數(shù)的類都有一個(gè)對(duì)應(yīng)的虛函數(shù)表。虛函數(shù)表是一個(gè)包含了虛函數(shù)指針的數(shù)組,其中每個(gè)虛函數(shù)都對(duì)應(yīng)一個(gè)函數(shù)指針,指向該虛函數(shù)在派生類中的實(shí)現(xiàn)。派生類的虛函數(shù)表會(huì)繼承基類的虛函數(shù)表,并可以通過重寫虛函數(shù)來修改其中的函數(shù)指針,從而實(shí)現(xiàn)對(duì)虛函數(shù)的覆蓋。
當(dāng)一個(gè)對(duì)象調(diào)用虛函數(shù)時(shí),C++ 編譯器會(huì)根據(jù)對(duì)象的類型在虛函數(shù)表中查找對(duì)應(yīng)的函數(shù)指針,并進(jìn)行函數(shù)調(diào)用。這種機(jī)制使得在運(yùn)行時(shí)可以根據(jù)對(duì)象的實(shí)際類型來決定調(diào)用哪個(gè)虛函數(shù),實(shí)現(xiàn)了多態(tài)性的特性。
需要注意的是,虛函數(shù)表是編譯器和鏈接器生成的,其具體實(shí)現(xiàn)可能因編譯器、操作系統(tǒng)和架構(gòu)而異。在 C++ 標(biāo)準(zhǔn)中并沒有對(duì)虛函數(shù)表的實(shí)現(xiàn)進(jìn)行規(guī)定,因此虛函數(shù)表的內(nèi)部結(jié)構(gòu)和使用方式可能在不同的編譯器和平臺(tái)上有所不同。
- C++ 虛函數(shù)表 如何實(shí)現(xiàn)多態(tài)的 解釋其中的實(shí)現(xiàn)和原理
- 虛函數(shù)的聲明和定義:在 C++ 中,通過在基類中將函數(shù)聲明為虛函數(shù),可以使其具有多態(tài)性。例如:
cppCopy code
class Base {
public:virtual void foo(); // 虛函數(shù)的聲明
};
在基類中聲明為虛函數(shù)的函數(shù)可以在派生類中被重寫(覆蓋)。
- 虛函數(shù)表的生成:當(dāng)包含虛函數(shù)的類被編譯時(shí),編譯器會(huì)為其生成虛函數(shù)表。虛函數(shù)表是一個(gè)包含了虛函數(shù)指針的數(shù)組,其中每個(gè)虛函數(shù)對(duì)應(yīng)一個(gè)函數(shù)指針,指向該虛函數(shù)在派生類中的實(shí)現(xiàn)。虛函數(shù)表通常由編譯器和鏈接器生成,并與類的對(duì)象分開存放。
- 虛函數(shù)調(diào)用的過程:當(dāng)一個(gè)對(duì)象調(diào)用虛函數(shù)時(shí),編譯器會(huì)根據(jù)對(duì)象的類型在虛函數(shù)表中查找對(duì)應(yīng)的函數(shù)指針,并進(jìn)行函數(shù)調(diào)用。這使得在運(yùn)行時(shí)可以根據(jù)對(duì)象的實(shí)際類型來決定調(diào)用哪個(gè)虛函數(shù),實(shí)現(xiàn)多態(tài)性的特性。具體的調(diào)用過程如下:
- 對(duì)象的內(nèi)存布局中通常包含一個(gè)指向虛函數(shù)表的指針(稱為虛函數(shù)表指針或 vptr),由編譯器在對(duì)象的構(gòu)造函數(shù)中進(jìn)行初始化。
- 當(dāng)對(duì)象調(diào)用虛函數(shù)時(shí),實(shí)際上是通過 vptr 在虛函數(shù)表中查找對(duì)應(yīng)的函數(shù)指針。
- 根據(jù)對(duì)象的實(shí)際類型,虛函數(shù)表中的函數(shù)指針可能指向基類中的虛函數(shù),也可能指向派生類中的覆蓋虛函數(shù)。
- 根據(jù)函數(shù)指針找到對(duì)應(yīng)的虛函數(shù)并進(jìn)行調(diào)用,從而實(shí)現(xiàn)多態(tài)性的效果。
- 派生類對(duì)虛函數(shù)的覆蓋:派生類可以通過在其定義中重新聲明虛函數(shù),從而對(duì)其進(jìn)行覆蓋。當(dāng)派生類重新聲明虛函數(shù)時(shí),編譯器會(huì)將該函數(shù)的地址更新到派生類的虛函數(shù)表中,從而使得派生類的虛函數(shù)表中的函數(shù)指針指向派生類中的實(shí)現(xiàn)。
總的來說,虛函數(shù)表通過將虛函數(shù)的地址集中管理,并在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類型來決定調(diào)用哪個(gè)虛函數(shù),從而實(shí)現(xiàn)了多態(tài)性的特性。這使得在編寫多態(tài)的代碼時(shí),可以通過基類指針或引用來操作派生類對(duì)象,從而實(shí)現(xiàn)靈活的對(duì)象行為和代碼復(fù)用
- 函數(shù)表的格式
虛函數(shù)表是一種包含了虛函數(shù)指針的數(shù)組,用于管理虛函數(shù)的調(diào)用。虛函數(shù)表的格式可以簡單描述如下:
class_name::vtable {// 虛函數(shù)指針1,指向虛函數(shù)1的地址// 虛函數(shù)指針2,指向虛函數(shù)2的地址// ...// 虛函數(shù)指針n,指向虛函數(shù)n的地址
}
其中,class_name
是類的名稱,vtable
是虛函數(shù)表的名稱。虛函數(shù)指針1到虛函數(shù)指針n 分別指向類中的虛函數(shù)1到虛函數(shù)n 的地址。
以下是一個(gè)簡單的例子,展示了虛函數(shù)表的格式和使用方式:
#include <iostream>class Base {
public:virtual void foo() {std::cout << "Base::foo()" << std::endl;}
};class Derived : public Base {
public:void foo() override {std::cout << "Derived::foo()" << std::endl;}
};int main() {Base* basePtr = new Derived(); // 使用基類指針指向派生類對(duì)象basePtr->foo(); // 調(diào)用虛函數(shù)delete basePtr;return 0;
}
在上面的例子中,Base
是基類,Derived
是派生類。Base
中聲明了虛函數(shù) foo()
,并在 Derived
中通過 override
關(guān)鍵字對(duì)其進(jìn)行了覆蓋。在 main()
函數(shù)中,通過基類指針 basePtr
指向 Derived
類的對(duì)象,并調(diào)用了虛函數(shù) foo()
。由于 foo()
是虛函數(shù),并且通過基類指針調(diào)用,所以會(huì)根據(jù)對(duì)象的實(shí)際類型來調(diào)用相應(yīng)的虛函數(shù),從而實(shí)現(xiàn)了多態(tài)性。在運(yùn)行時(shí),會(huì)根據(jù) basePtr
指向的對(duì)象是 Derived
類的對(duì)象,因此輸出結(jié)果為 Derived::foo()
。這就是通過虛函數(shù)表來實(shí)現(xiàn)多態(tài)性的原理。
在 C++ 中,當(dāng)一個(gè)對(duì)象調(diào)用虛函數(shù)時(shí),編譯器不會(huì)在編譯時(shí)確定調(diào)用哪個(gè)函數(shù),而是在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類型來查找虛函數(shù)表(Virtual Function Table,簡稱 vtable)中對(duì)應(yīng)的函數(shù)指針,并進(jìn)行函數(shù)調(diào)用。這樣就實(shí)現(xiàn)了多態(tài)性的特性,即同一段代碼可以在不同的對(duì)象上表現(xiàn)出不同的行為。
以下是一個(gè)簡單的示例代碼,用于說明虛函數(shù)和虛函數(shù)表的概念:
#include <iostream>class Animal {
public:virtual void makeSound() {std::cout << "Animal makes sound." << std::endl;}
};class Dog : public Animal {
public:void makeSound() override {std::cout << "Dog barks." << std::endl;}
};class Cat : public Animal {
public:void makeSound() override {std::cout << "Cat meows." << std::endl;}
};int main() {Animal* animal1 = new Animal();Animal* animal2 = new Dog();Animal* animal3 = new Cat();animal1->makeSound(); // 輸出:Animal makes sound.animal2->makeSound(); // 輸出:Dog barks.animal3->makeSound(); // 輸出:Cat meows.delete animal1;delete animal2;delete animal3;return 0;
}
在上面的示例代碼中,定義了一個(gè)基類 Animal
和兩個(gè)派生類 Dog
和 Cat
,它們都有一個(gè)虛函數(shù) makeSound()
。在 main()
函數(shù)中,分別創(chuàng)建了三個(gè)對(duì)象,分別是 Animal
類型、Dog
類型和 Cat
類型的指針,并通過調(diào)用 makeSound()
函數(shù)來觀察不同對(duì)象的行為。由于 makeSound()
是虛函數(shù),因此在運(yùn)行時(shí)會(huì)根據(jù)對(duì)象的實(shí)際類型來查找對(duì)應(yīng)的函數(shù)指針,并進(jìn)行函數(shù)調(diào)用,從而實(shí)現(xiàn)了多態(tài)性的特性。
在 C++ 中,編譯器通過在每個(gè)包含虛函數(shù)的類中生成一個(gè)虛函數(shù)表(vtable)來實(shí)現(xiàn)多態(tài)性。虛函數(shù)表是一個(gè)包含函數(shù)指針的表格,用于存儲(chǔ)每個(gè)虛函數(shù)的地址。每個(gè)對(duì)象都有一個(gè)指向其類的虛函數(shù)表的指針,稱為虛函數(shù)表指針(vptr)。當(dāng)對(duì)象調(diào)用虛函數(shù)時(shí),實(shí)際上是通過虛函數(shù)表指針在虛函數(shù)表中查找對(duì)應(yīng)的函數(shù)指針,并進(jìn)行函數(shù)調(diào)用。
下面是一個(gè)簡化的示意圖,說明了虛函數(shù)表的概念:
luaCopy code+-----------------------+| vptr (虛函數(shù)表指針) |+-----------------------+| 虛函數(shù)表 |+-----------------------+| 函數(shù)指針1 |+-----------------------+| 函數(shù)指針2 |+-----------------------+| ... |+-----------------------+
當(dāng)一個(gè)對(duì)象調(diào)用虛函數(shù)時(shí),首先會(huì)通過對(duì)象的虛函數(shù)表指針找到虛函數(shù)表的地址,然后根據(jù)函數(shù)在虛函數(shù)表中的索引找到對(duì)應(yīng)的函數(shù)指針,最后通過函數(shù)指針進(jìn)行函數(shù)調(diào)用。
以下是一個(gè)簡單的示例代碼,演示了虛函數(shù)表的實(shí)現(xiàn)方式:
#include <iostream>class Animal {
public:virtual void makeSound() {std::cout << "Animal makes sound." << std::endl;}
};class Dog : public Animal {
public:void makeSound() override {std::cout << "Dog barks." << std::endl;}
};int main() {Animal* animal = new Dog();animal->makeSound(); // 輸出:Dog barks.delete animal;return 0;
}
在上面的示例代碼中,Animal
類和 Dog
類都有一個(gè)虛函數(shù) makeSound()
。當(dāng)通過 new
運(yùn)算符創(chuàng)建了一個(gè) Dog
對(duì)象,并通過 Animal*
類型的指針調(diào)用 makeSound()
函數(shù)時(shí),實(shí)際上是通過 animal
對(duì)象的虛函數(shù)表指針找到 Dog
類的虛函數(shù)表,并通過函數(shù)指針進(jìn)行函數(shù)調(diào)用,從而輸出了 “Dog barks.”。這就是虛函數(shù)表的實(shí)現(xiàn)方式,通過在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類型查找對(duì)應(yīng)的函數(shù)指針,從而實(shí)現(xiàn)了多態(tài)性的特性。
下面是一個(gè)簡化的示意圖,說明了父類和子類在虛函數(shù)表中的概念:
+-------------------------------------+| 父類的虛函數(shù)表 |+-------------------------------------+| 函數(shù)指針1 (父類虛函數(shù)) |+-------------------------------------+| 函數(shù)指針2 (父類虛函數(shù)) |+-------------------------------------+| ... |+-------------------------------------++-------------------------------------+| 子類的虛函數(shù)表 |+-------------------------------------+| 函數(shù)指針1 (子類虛函數(shù)) |+-------------------------------------+| 函數(shù)指針2 (子類虛函數(shù)) |+-------------------------------------+| 函數(shù)指針3 (子類新增虛函數(shù)) |+-------------------------------------+| ... |+-------------------------------------+
在上面的示意圖中,左側(cè)是父類的虛函數(shù)表,右側(cè)是子類的虛函數(shù)表。父類的虛函數(shù)表中包含了父類的虛函數(shù),子類的虛函數(shù)表中包含了子類的虛函數(shù),以及可能的新增虛函數(shù)。
當(dāng)子類繼承自父類并且覆蓋(override)了父類的虛函數(shù)時(shí),子類會(huì)在自己的虛函數(shù)表中存儲(chǔ)覆蓋后的函數(shù)指針,而不會(huì)影響到父類的虛函數(shù)表。這就是 C++ 中虛函數(shù)的覆蓋(override)機(jī)制。
當(dāng)通過父類的指針或引用調(diào)用虛函數(shù)時(shí),實(shí)際上會(huì)根據(jù)對(duì)象的實(shí)際類型在虛函數(shù)表中查找對(duì)應(yīng)的函數(shù)指針,并進(jìn)行函數(shù)調(diào)用。如果對(duì)象是父類類型的,則會(huì)調(diào)用父類的虛函數(shù);如果對(duì)象是子類類型的,則會(huì)調(diào)用子類的虛函數(shù),包括覆蓋了父類虛函數(shù)和子類新增的虛函數(shù)。這樣實(shí)現(xiàn)了多態(tài)性的特性,可以在運(yùn)行時(shí)根據(jù)對(duì)象的實(shí)際類型動(dòng)態(tài)地調(diào)用相應(yīng)的虛函數(shù)。
在 C++ 中,虛函數(shù)表(vtable)是由編譯器在編譯階段生成的,其內(nèi)部實(shí)現(xiàn)可能因編譯器和平臺(tái)的不同而有所差異。虛函數(shù)表中的函數(shù)指針的排列方式也可能因編譯器和平臺(tái)而有所不同,但一般來說,虛函數(shù)表中的函數(shù)指針是按照聲明順序排列的。
對(duì)于上面的示例中的 Cat
類,假設(shè)編譯器將其虛函數(shù)表中的函數(shù)指針按照聲明順序排列,可能的虛函數(shù)表的排列方式如下:
Cat vtable:
-------------------------------------------------
| Animal::makeSound() | Cat::makeSound() |
-------------------------------------------------
這里假設(shè) Animal::makeSound()
是 Animal
類中的虛函數(shù),Cat::makeSound()
是 Cat
類中覆蓋了父類虛函數(shù)的函數(shù)。虛函數(shù)表中的第一個(gè)函數(shù)指針指向 Animal::makeSound()
,第二個(gè)函數(shù)指針指向 Cat::makeSound()
。
需要注意的是,虛函數(shù)表的具體實(shí)現(xiàn)方式可能因編譯器和平臺(tái)而有所不同,以上只是一種可能的示意圖,實(shí)際情況可能會(huì)有所不同。編譯器和平臺(tái)可能會(huì)使用不同的優(yōu)化技術(shù)和內(nèi)存布局來實(shí)現(xiàn)虛函數(shù)表。