網(wǎng)站首頁 編程語言 正文
析構(gòu)函數(shù)為什么是虛函數(shù)
虛構(gòu)函數(shù)是虛函數(shù)的情況只需要在特定場景下出現(xiàn)即可,正常情況下不必要弄成虛函數(shù)。
如果基類的析構(gòu)函數(shù)不是虛函數(shù),在特定情況下會導(dǎo)致派生來無法被析構(gòu)。
- 情況1:用派生類類型指針綁定派生類實例,析構(gòu)的時候,不管基類析構(gòu)函數(shù)是不是虛函數(shù),都會正常析構(gòu)
- 情況2:用基類類型指針綁定派生類實例,析構(gòu)的時候,如果基類析構(gòu)函數(shù)不是虛函數(shù),則只會析構(gòu)基類,不會析構(gòu)派生類對象,從而造成內(nèi)存泄漏。為什么???因為析構(gòu)的時候如果沒有虛函數(shù)的動態(tài)綁定功能,就只根據(jù)指針的類型來進行的,而不是根據(jù)指針綁定的對象來進行,所以只是調(diào)用了基類的析構(gòu)函數(shù);如果基類的析構(gòu)函數(shù)是虛函數(shù),則析構(gòu)的時候就要根據(jù)指針綁定的對象來調(diào)用對應(yīng)的析構(gòu)函數(shù)了。
構(gòu)造函數(shù)為什么不能出虛函數(shù)
因為類的虛函數(shù)表指針是在構(gòu)造函數(shù)中初始化的,這時候如果構(gòu)造函數(shù)本身是虛函數(shù),又應(yīng)該由誰來初始化它的虛函數(shù)指針呢,所以構(gòu)造函數(shù)不能是虛函數(shù)。
為什么構(gòu)造函數(shù)和析構(gòu)函數(shù)都不能調(diào)用虛函數(shù)
?? ?#include <iostream.h>
? ? using namespace std;
? ? class Base{
? ? ? ? public:
? ? ? ? ? ? Base(){
? ? ? ? ? ? ? ? cout << "Base::Base()\n";
? ? ? ? ? ? ? ? fun();
? ? ? ? ? ? }
? ? ? ? ? ? virtual ~Base(){
? ? ? ? ? ? ? ? cout << "Base::Base()\n";
? ? ? ? ? ? ? ? fun();
? ? ? ? ? ? }
? ? ? ? ? ? virtual void fun(){
? ? ? ? ? ? ? ? cout << "Base::fun() virtual\n";
? ? ? ? ? ? }
? ? };
? ? // 派生類
? ? class Derive: public Base{
? ? ? ? public:
? ? ? ? ? ? Derive(){
? ? ? ? ? ? ? ? cout << "Derive::Derive()\n";
? ? ? ? ? ? ? ? fun();
? ? ? ? ? ? }
? ? ? ? ? ? ~Derive(){
? ? ? ? ? ? ? ? cout << "Derive::Derive()\n";
? ? ? ? ? ? ? ? fun();
? ? ? ? ? ? }
? ? ? ? ? ? virtual void fun(){
? ? ? ? ? ? ? ? cout << "Derive::fun() virtual\n";
? ? ? ? ? ? }
? ? };
? ? int main()
? ? {
? ? ? ? Base *bd = new Derive(); ?// 基類Base的指針bd指向的是派生類Derive的對象
? ? ? ? delete bd;
? ? ? ? return 0;
? ? }
對于上述情況,基類指針指向派生類對象。構(gòu)造時,先調(diào)用基類Base的構(gòu)造函數(shù),此時構(gòu)函數(shù)中調(diào)用基類中的fun()函數(shù),此時虛函數(shù)的動態(tài)綁定機制并沒有會生效,這是因為此時的派生類還不存在。析構(gòu)時,先析構(gòu)派生類,派生類中的fun()函數(shù)調(diào)用的是自己的fun(),然后析構(gòu)基類Base,基類析構(gòu)函數(shù)中的fun()調(diào)用的是基類Base自己的fun()函數(shù),這里虛函數(shù)的動態(tài)綁定機制也沒有生效,因為此時派生類已經(jīng)不存在了。
不要在構(gòu)造函數(shù)中調(diào)用虛函數(shù)的原因:因為父類對象會在子類之前進行構(gòu)造,此時子類部分的數(shù)據(jù)成員還未初始化, 因此調(diào)用子類的虛函數(shù)是不安全的,故而C++不會進行動態(tài)聯(lián)編。
不要在析構(gòu)函數(shù)中調(diào)用虛函數(shù)的原因:析構(gòu)函數(shù)是用來銷毀一個對象的,在銷毀一個對象時,先調(diào)用子類的析構(gòu)函數(shù),然后再調(diào)用基類的析構(gòu)函數(shù)。所以在調(diào)用基類的析構(gòu)函數(shù)時,派生類對象的數(shù)據(jù)成員已經(jīng)“銷毀”,這個時再調(diào)用子類的虛函數(shù)已經(jīng)沒有意義了。
c++基類的析構(gòu)函數(shù)為虛函數(shù)的原因
原因
在實現(xiàn)多態(tài)時, 當(dāng)用基類指針操作派生類, 在析構(gòu)時候防止只析構(gòu)基類而不析構(gòu)派生類。
例子
(1):
#include<iostream>
using namespace std;
class Base{
public:
? ?Base() {};
? ~Base() {cout << "Output from the destructor of class Base!" << endl;};
?
? void DoSomething() { cout << "Do something in class Base!" << endl; };
};
?
class Derived : public Base{
public:
? ?Derived() {};
? ~Derived() { cout << "Output from the destructor of class Derived!" << endl; };
?
? void DoSomething() { cout << "Do something in class Derived!" << endl; };
};
? int ?main()
? ?{?
? Derived* p = new Derived;
?p->DoSomething();
?delete p;
?return 0;
}
運行結(jié)果:
Do something in class Derived! ? ? ? ? ??
Output from the destructor of class Derived!
Output from the destructor of class Base!?
代碼中基類的析構(gòu)函數(shù)不是虛函數(shù),在main函數(shù)中用繼承類的指針去操作繼承類的成員,釋放指針P的過程是:先釋放繼承類的資源,再釋放基類資源。
(2):
?#include<iostream>
using namespace std;
class Base{
public:
? ?Base() {};
? ~Base() {cout << "Output from the destructor of class Base!" << endl;};
?
? void DoSomething() { cout << "Do something in class Base!" << endl; };
};
class Derived : public Base{
public:
? ?Derived() {};
? ~Derived() { cout << "Output from the destructor of class Derived!" << endl; };
?
? void DoSomething() { cout << "Do something in class Derived!" << endl; };
};
? int ?main(){?
? Base* p = new Derived;
?p->DoSomething();
?delete p;
?return 0;
?}
運行結(jié)果:
Do something in class ClxBase!
Output from the destructor of class ClxBase!
代碼中基類的析構(gòu)函數(shù)同樣不是虛函數(shù),不同的是在main函數(shù)中用基類的指針去操作繼承類的成員,釋放指針P的過程是:只釋放基類的資源,而沒有調(diào)用繼承類的析構(gòu)函數(shù)。 調(diào)用DoSomething()函數(shù)執(zhí)行的也是基類定義的函數(shù)。
一般情況下,這樣的刪除只能夠刪除基類對象,而不能刪除子類對象,形成了刪除一半形象,造成內(nèi)存泄漏。
在公有繼承中,基類對派生類及其對象的操作,只能影響到那些從基類繼承下來的成員。如果想要用基類對非繼承成員進行操作,則要把基類的這個函數(shù)定義為虛函數(shù)。 析構(gòu)函數(shù)自然也應(yīng)該如此:如果它想析構(gòu)子類中的重新定義或新的成員及對象,當(dāng)然也應(yīng)該聲明為虛的。
(3):
#include<iostream>
using namespace std;
class Base{
public:
? ?Base() {};
? virtual ~Base() {cout << "Output from the destructor of class Base!" << endl;};
?
? virtual void DoSomething() { cout << "Do something in class Base!" << endl; };
};
class Derived : public Base{
public:
? ?Derived() {};
? ~Derived() { cout << "Output from the destructor of class Derived!" << endl; };
?
? void DoSomething() { cout << "Do something in class Derived!" << endl; };
};
? int ?main(){?
? Base* p = new Derived;
?p->DoSomething();
?delete p;
?return 0;
?}
運行結(jié)果:
Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
Output from the destructor of class ClxBase!
代碼中基類的析構(gòu)函數(shù)被定義為虛函數(shù),在main函數(shù)中用基類的指針去操作繼承類的成員,釋放指針P的過程是:釋放了繼承類的資源,再調(diào)用基類的析構(gòu)函數(shù)。調(diào)用DoSomething()函數(shù)執(zhí)行的也是繼承類定義的函數(shù)。
小結(jié):基類指針可以指向派生類的對象(多態(tài)性),如果刪除該指針delete p;就會調(diào)用該指針指向的派生類析構(gòu)函數(shù),而派生類的析構(gòu)函數(shù)又自動調(diào)用基類的析構(gòu)函數(shù),這樣整個派生類的對象完全被釋放。
如果析構(gòu)函數(shù)不被聲明成虛函數(shù),則編譯器實施靜態(tài)綁定,在刪除基類指針時,只會調(diào)用基類的析構(gòu)函數(shù)而不調(diào)用派生類析構(gòu)函數(shù),這樣就會造成派生類對象析構(gòu)不完全。所以,將析構(gòu)函數(shù)聲明為虛函數(shù)是十分必要的。
總結(jié)
原文鏈接:https://blog.csdn.net/aixiaodeshushu/article/details/100009087
相關(guān)推薦
- 2022-04-15 python字符串操作詳析_python
- 2023-02-02 Go并發(fā)與鎖的兩種方式該如何提效詳解_Golang
- 2022-06-17 C#中Parallel類For、ForEach和Invoke使用介紹_C#教程
- 2022-03-27 C語言中scanf與scnaf_s函數(shù)詳解_C 語言
- 2022-12-24 Kotlin?Channel處理多個數(shù)據(jù)組合的流_Android
- 2023-02-09 利用C++開發(fā)一個protobuf動態(tài)解析工具_C 語言
- 2022-07-29 Linux中Vi和Vim編輯器用法詳解_linux shell
- 2022-05-29 Python實現(xiàn)灰色關(guān)聯(lián)分析與結(jié)果可視化的詳細代碼_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支