C++虛函數(shù)表和虛析構(gòu)介紹
1、虛函數(shù)表
虛函數(shù)表是C++實現(xiàn)多態(tài)的基礎(chǔ),多態(tài)是面向?qū)ο蟮娜筇匦灾?,多態(tài)有利于提高代碼的可讀性,便于后期代碼的擴(kuò)展和維護(hù)。我們都知道多態(tài)的實現(xiàn)是基于虛函數(shù)表,那么虛函數(shù)表是什么時候創(chuàng)建的呢?虛函數(shù)表是怎么實現(xiàn)多態(tài)的功能的呢?
首先應(yīng)該明確多態(tài)也稱為動態(tài)多態(tài),他是在程序運行時候確定函數(shù)地址的,也就是程序在運行時,如果類成員函數(shù)加了virtual關(guān)鍵字,就會建立一個虛函數(shù)指針(vfptr)指針指向一個虛函數(shù)表,這個虛函數(shù)表就保存了虛函數(shù)的地址,子類繼承父類也自然繼承了虛函數(shù)指針,當(dāng)子類重寫父類的虛函數(shù)時,虛函數(shù)指針?biāo)赶虻奶摵瘮?shù)表中的虛函數(shù)地址就會被覆蓋,替換成子類的虛函數(shù)地址。也就是通過父類的虛函數(shù)指針找到了子類的虛函數(shù)地址,進(jìn)而執(zhí)行這個函數(shù)。
下面我們通過代碼進(jìn)行詳細(xì)說明:
#include <iostream>
using namespace std;
class Base{
public:
void func(){
cout << "Base func" << endl;
}
};
class Son: public Base{
void func(){
cout << "Son func" << endl;
}
};
void test(Base& base) {
base.func();
}
int main () {
Son son;
cout << "sizeof(Base) = " << sizeof(Base) << endl;
cout << "sizeof(Son) = " << sizeof(Son) << endl;
test(son);
system("pause");
return 0;
}
代碼運行結(jié)果為:

因為函數(shù)成員不占用類的大小,所以對Base類和Son類輸出大小,都是一個字節(jié),這一個字節(jié)是為了可以實例化類,通過引用基類引用派生類,調(diào)用func函數(shù),函數(shù)調(diào)用了基類的func,那么如果我們加上virtual關(guān)鍵字后,就不是這種情況了。
#include <iostream>
using namespace std;
class Base{
public:
virtual void func(){
cout << "Base func" << endl;
}
};
class Son: public Base{
void func(){
cout << "Son func" << endl;
}
};
void test(Base& base) {
base.func();
}
int main () {
Son son;
cout << "sizeof(Base) = " << sizeof(Base) << endl;
cout << "sizeof(Son) = " << sizeof(Son) << endl;
test(son);
system("pause");
return 0;
}
代碼運行結(jié)果為:

可以看到加了virtual關(guān)鍵字后,父類和子類的大小都變成了四字節(jié),這是因為生成了虛函數(shù)指針,指針指向虛函數(shù)表,虛函數(shù)表存儲了虛函數(shù)地址,繼承了父類的子類重寫了虛函數(shù),虛函數(shù)表中的函數(shù)地址被替換,再次調(diào)用虛函數(shù)就是調(diào)用了子類的函數(shù)func。
2、虛析構(gòu)
虛析構(gòu)主要是為了解決子類中有屬性開辟到堆區(qū),父類指針調(diào)用函數(shù)時,無法調(diào)用到子類的析構(gòu)代碼,導(dǎo)致子類堆區(qū)內(nèi)存無法釋放。
首先我們看一下子類堆區(qū)內(nèi)存開辟,通過父類指針來調(diào)用函數(shù),捕捉他們的構(gòu)造函數(shù)和析構(gòu)函數(shù)看下運行結(jié)果:
#include <iostream>
using namespace std;
class Base{
public:
Base(){
cout << "Base 的構(gòu)造函數(shù)調(diào)用" << endl;
}
~Base(){
cout << "Base 的析構(gòu)函數(shù)調(diào)用" << endl;
}
virtual void func(){
cout << "Base func" << endl;
}
};
class Son: public Base{
public:
Son(int val):m_val(new int (val)) {
cout << "Son 的構(gòu)造函數(shù)調(diào)用" << endl;
}
~Son(){
cout << "Son 的析構(gòu)函數(shù)調(diào)用" << endl;
if (m_val != NULL) {
delete m_val;
cout << "Son 析構(gòu)函數(shù)的堆內(nèi)存釋放" << endl;
m_val = NULL;
}
}
void func(){
cout << "Son func" << endl;
}
void funcTest(){
cout << "funcTest 函數(shù)調(diào)用" << endl;
}
int* m_val = NULL;
};
void test() {
Base *base = new Son(10);
base->func();
//base->funcTest(); //無法調(diào)用,因為虛函數(shù)表中不能找到這個函數(shù)的地址
delete base;
base = NULL;
}
int main () {
test();
system("pause");
return 0;
}
代碼運行結(jié)果為:

可以明確,通過父類指針來調(diào)用函數(shù)的時候,無法調(diào)用Son類的析構(gòu)函數(shù),在Son類在堆區(qū)上申請的內(nèi)存就無法釋放,造成內(nèi)存泄漏。Son類的析構(gòu)函數(shù)不能調(diào)用的主要原因就是在虛函數(shù)表中找不到Son的析構(gòu)函數(shù)地址,解決辦法就是把Base類的寫成虛析構(gòu)函數(shù)或者純虛析構(gòu)函數(shù),
下面給出Base類為純虛析構(gòu)函數(shù)的代碼和運行結(jié)果:
#include <iostream>
using namespace std;
class Base{
public:
Base(){
cout << "Base 的構(gòu)造函數(shù)調(diào)用" << endl;
}
virtual ~Base() = 0;
virtual void func(){
cout << "Base func" << endl;
}
};
Base :: ~Base(){
cout << "Base 的析構(gòu)函數(shù)調(diào)用" << endl;
}
class Son: public Base{
public:
Son(int val):m_val(new int (val)) {
cout << "Son 的構(gòu)造函數(shù)調(diào)用" << endl;
}
~Son(){
cout << "Son 的析構(gòu)函數(shù)調(diào)用" << endl;
if (m_val != NULL) {
delete m_val;
cout << "Son 析構(gòu)函數(shù)的堆內(nèi)存釋放" << endl;
m_val = NULL;
}
}
void func(){
cout << "Son func" << endl;
}
void funcTest(){
cout << "funcTest 函數(shù)調(diào)用" << endl;
}
int* m_val = NULL;
};
void test() {
Base *base = new Son(10);
base->func();
//base->funcTest(); //無法調(diào)用,因為虛函數(shù)表中不能找到這個函數(shù)的地址
delete base;
base = NULL;
}
int main () {
test();
system("pause");
return 0;
}
代碼運行結(jié)果為:

可以看到只要把Base類的析構(gòu)函數(shù)寫成虛析構(gòu)函數(shù)或純虛析構(gòu)函數(shù),通過父類指針調(diào)用函數(shù),子類的析構(gòu)代碼會被調(diào)用,子類堆區(qū)內(nèi)存得到釋放。
到此這篇關(guān)于C++虛函數(shù)表和虛析構(gòu)介紹的文章就介紹到這了,更多相關(guān)C++虛函數(shù)表和虛析構(gòu)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c++?error:crosses?initialization?of問題解決分析
這篇文章主要介紹了c++?error:crosses?initialization?ofde?問題解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
CLion搭建配置C++開發(fā)環(huán)境的圖文教程 (MinGW-W64 GCC-8.1.0)
這篇文章主要介紹了CLion搭建配置C++開發(fā)環(huán)境的教程 (MinGW-W64 GCC-8.1.0),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02
數(shù)據(jù)結(jié)構(gòu)之歸并排序的實例詳解
這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu)之歸并排序的實例詳解的相關(guān)資料,這里對歸并排序進(jìn)行詳細(xì)介紹,需要的朋友可以參考下2017-08-08

