12、虛函數的應用、虛析構函數

12、虛函數的應用、虛析構函數

  • 運行時類型信息(RTTI)
    • 動態類型轉換(dynamic_cast)
    • typeid操作符
  • 虛 析構函數
    • 空虛析構函數

一個類中,除了構造函數和靜態成員函數外,任何函數都可以被聲明為虛函數

運行時類型信息(RTTI)

動態類型轉換(dynamic_cast)

  • 用于將基類類型的指針或引用轉換為其子類類型的指針或引用
  • 前提是子類必須從基類多態繼承 (即基類包含至少一個虛函數)
  • 動態類型轉換會對所需轉換的基類指針或引用做檢查,如果其指向的對象的類型與所要轉換的目標類型一致,則轉換成功,否則轉換失敗。
  • 針對指針的動態類型轉換,以返回空指針(NULL)表示失敗,針對引用的動態類型轉換以拋出bad_cast異常表示失敗
// 動態類型轉換 :基類類型指針轉換為子類類型指針
//                基類類型引用轉換為子類類型引用
#include <iostream>
using namespace std;class A { // 編譯器根據A類信息,將制作一張虛函數表 "A"...|A::foo的地址 
public:virtual void foo(){}
};class B : public A { // 編譯器根據B類信息,將制作一張虛函數表 "B"...|A::foo的地址
};class C : public B { // 編譯器根據C類信息,將制作一張虛函數表 "C"...|A::foo的地址
};class D {}; // 編譯器根據D類信息,不制作虛函數表int main( void ){B b; // |虛表指針| --> 編譯器根據B類信息制作的虛函數表A* pa = &b; // B* --> A* (子類類型指針 --> 基類類型指針)cout << "---------------------dynamic_cast 運行期間做的轉換-----------------------" << endl; B* pb = dynamic_cast<B*>(pa); // pa-->b對象所占內存空間-->虛表指針 --> 編譯器根據B類信息制作的虛函數表->"B"cout << "A* pa --> B* pb: " << pb << endl;C* pc = dynamic_cast<C*>(pa); // pa-->b對象所占內存空間-->虛表指針 --> 編譯器根據B類信息制作的虛函數表->"B"cout << "A* pa --> C* pc: " << pc << endl;D* pd = dynamic_cast<D*>(pa); // pa-->b對象所占內存空間-->虛表指針 --> 編譯器根據B類信息制作的虛函數表->"B"cout << "A* pa --> D* pd: " << pd << endl;cout << "---------------------static_cast 編譯期間做的轉換-----------------------" << endl; pb = static_cast<B*>(pa); // 即合理且安全 A* -->B*的反向 可以隱式轉換cout << "A* pa --> B* pb: " << pb << endl;pc = static_cast<C*>(pa); // 有風險 A*-->C*的反向 可以隱式轉換cout << "A* pa --> C* pc: " << pc << endl;//  pd = static_cast<D*>(pa); // 不合理 A*-->D*的反向 不可以隱式轉換
//  cout << "A* pa --> D* pd: " << pd << endl;return 0;
} 

typeid操作符

  • #include <typeinfo>
  • 返回type info類型對象的常引用
    • type info類的成員函數name(),返回類型名字符串
    • type info類支持“==”和“!=”操作符,可直接用于類型相同與否的判斷
  • 當其作用于基類類型的指針或引用的目標對象時
    • 若基類不包含虛函數 typeid所返回類型信息由該指針或引用本身的類型決定
    • 若基類包含至少一個虛函數,即存在多態繼承,typeid所返回類型信息由該指針或引用的實際目標對象的類型決定
// typeid操作符 -- 獲取對象的類型信息
//                 無法獲取對象常屬性信息          
#include <iostream>
#include <typeinfo>
using namespace std;class A { // 編譯器根據A類信息,將制作一張虛函數表 "A"...|A::foo的地址 virtual void foo(){}
};class B : public A { //  編譯器根據B類信息,將制作一張虛函數表 "B"...|A::foo的地址 
};int main( void ){B b;// |虛表指針| --> 編譯器根據B類信息制作的虛函數表A* pa = &b;A& ra = b;cout << "pa指針的目標對象的類型:" << typeid(*pa).name() << endl;// pa->b對象所占內存空間-->虛表指針-->B類虛函數表-->"B"cout << "ra引用的目標對象的類型:" << typeid(ra).name() << endl;// ra->b對象所占內存空間-->虛表指針-->B類虛函數表-->"B"int m;const type_info& rty = typeid(m);// 1. 獲取m的類型信息(類名、類大小、類版本...)// 2. 創建一個type_info類對象// 3. 將獲取到的m的類型信息保存到type_info對象的私有成員變量中// 4. 返回type_info類對象的常引用string rn = rty.name();cout << "m的類型:" << rn << endl;const int n = 10;cout << "n的類型:" << typeid(n).name() << endl;cout << (typeid(m) == typeid(n)) << endl;cout << (typeid(m)!=typeid(n)) << endl;return 0;
} 

虛 析構函數

delete一個基類指針 (指向子類對象)

  • 實際被調用的僅僅是基類的析構函數

  • 基類的析構函數只負責析構子類對象中的基類子對象

  • 基類的析構函數不會調用子類的析構函數

  • 在子類中分配的資源將無法得到釋放

  • 如果將基類的析構函數聲明為虛函數,那么實際被調用的將是子類的析構函數

  • 子類的析構函數將首先釋放子類對象自己的成員,然后再調用基類的析構函數釋放該子類對象的基類部分,最終實現完美的資源釋放

// 虛析構函數 -- delete一個基類類型指針(指向子類對象),能夠正確的調用子類的析構函數
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
using namespace std;class A{
public:A():m_a(open("./file",O_CREAT | O_RDWR, 0644)){//【int m_a = open(..);】定義m_a,初值為文件描述符 -->文件表等內核信息(動態資源)cout << "A()被調用 -- 打開file文件" << endl;}virtual ~A(){ // 虛析構函數close(m_a);cout << "~A()被調用 -- 關閉file文件" << endl;// 釋放m_a本身所占內存空間}
private:int m_a;
};
class B : public A{
public:B():m_b(open("./cfg",O_CREAT | O_RDWR, 0644)){//【A();】定義基類子對象,利用基類子對象.A()//【int m_b = open(...);】定義m_b,初值為文件描述符-->文件表等內核信息(動態資源)cout << "B()被調用 -- 打開cfg文件" << endl;}~B(){  // 虛析構函數close(m_b);cout << "~B()被調用 -- 關閉cfg文件" << endl;// 對于基類子對象,利用基類子對象.~A()// 釋放m_b/基類子對象本身所占內存空間}
private:int m_b;
};int main( void ){A* p = new B; // 定義B堆對象,利用B類堆對象.B()delete p; // p->析構函數(~B()) 釋放B類堆對象本身所占內存空間return 0;
} 

空虛析構函數

  • 沒有分配任何動態資源的類,無需定義析構函數
  • 沒有定義析構函數的類,編譯器會為其提供一個缺省析構函數,但缺省析構函數并不是虛函數
  • 為了保證delete一個指向子類對象的基類指針時,能夠正確調用子類的析構函數,就必須把基類的析構函數定義為虛函數,即使它是一個空函數
  • 任何時候,為基類定義一個虛析構函數總是無害的

一個類中,除了構造函數和靜態成員函數外,任何函數都可以被聲明為虛函數

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/212543.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/212543.shtml
英文地址,請注明出處:http://en.pswp.cn/news/212543.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AMC8美國數學競賽歷年真題集在線練習操作指南和2024年備考建議

今天是2023年12月10日&#xff0c;距離2024年的AMC8美國數學競賽的舉辦還有40天時間。據六分成長了解&#xff0c;有一些孩子報名參加了AMC8的機構培訓班系統學習&#xff0c;也有一些孩子選擇了自己自學備考。 有家長問AMC8的培訓是否一定要參加機構的培訓班學習&#xff1f;…

基于SpringBoot+thymeleaf協同過濾算法山河旅游推薦系統(Java畢業設計)

大家好&#xff0c;我是DeBug&#xff0c;很高興你能來閱讀&#xff01;作為一名熱愛編程的程序員&#xff0c;我希望通過這些教學筆記與大家分享我的編程經驗和知識。在這里&#xff0c;我將會結合實際項目經驗&#xff0c;分享編程技巧、最佳實踐以及解決問題的方法。無論你是…

windows端口被占用怎么辦 怎么關閉那個占用的端口

目錄 這是出現的情況怎么解決了1.請打開這玩意2.輸入下面---查詢 先關端口的信息根據id獲得服務 上圖的8888 對應的ip 上圖就是134243.殺死進程134244.重啟服務 這是出現的情況 怎么解決了 1.請打開這玩意 2.輸入下面—查詢 先關端口的信息 netstat -ano過濾信息查詢想要的端…

JavaScript將函數作為參數傳入

其他函數中&#xff0c;是一種常見的編程技巧&#xff0c;稱為回調函數。在 JavaScript 中&#xff0c;函數被視為一等公民&#xff0c;也就是說&#xff0c;它們可以像任何其他類型的值一樣被傳遞、分配和操作。 示例&#xff1a; function greet(name) {console.log(Hello …

央企國企相關

文章目錄&#xff1a; 一&#xff1a;央企國企的區別 二&#xff1a;分類 三&#xff1a;相關 1.考什么 2.有什么崗位 3.什么時候考 4.去哪里報名和查看信息 5.喜歡招聘什么專業 6.其他疑問 一&#xff1a;央企國企的區別 央企國企一共有47萬多個&#xff08;央企131個…

【8.0.34-0 ubuntu 安裝Mysql 后無法鏈接是什么情況】

8.0.34-0 ubuntu 安裝Mysql 后無法鏈接是什么情況 檢查日志解決辦法 檢查日志 如果檢查一下帳號密碼沒問題看一下日志&#xff1a; Plugin mysql_native_password reported: mysql_native_password is deprecated and will be removed in a future release. Please use cachi…

java中的context對象?

java中的context對象&#xff1f; 大家好&#xff0c;我是微賺淘客系統的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01;今天&#xff0c;我們將深入研究Java中的神秘利器——Context對象。在Java開發中&#xff0c;Context對象扮演著重要的角…

排序算法之六:快速排序(遞歸)

快速排序的基本思想 快速排序是Hoare于1962年提出的一種二叉樹結構的交換排序方法 其基本思想為&#xff1a; 任取待排序元素序列中的某元素作為基準值&#xff0c;按照該排序碼將待排序集合分割成兩子序列&#xff0c;左子序列中所有元素均小于基準值&#xff0c;右序列中所…

《深入理解計算機系統》學習筆記 - 第四課 - 浮點數

Floating Point 浮點數 文章目錄 Floating Point 浮點數分數二進制示例能代表的數浮點數的表示方式浮點數編碼規格化值規格化值編碼示例 非規格化的值特殊值 示例IEEE 編碼的一些特殊屬性四舍五入&#xff0c;相加&#xff0c;相乘四舍五入四舍五入的模式二進制數的四舍五入 浮…

【Qt5】setWindowFlags的標志有哪些?

2023年12月9日&#xff0c;周六晚上 窗口類型&#xff1a; Widget&#xff08;0x00000000&#xff09;&#xff1a;普通窗口部件。Window&#xff08;0x00000001&#xff09;&#xff1a;標準窗口。Dialog&#xff08;0x00000002 | Window&#xff09;&#xff1a;對話框&#…

UI自動化Selenium 鼠標滑動懸停到指定元素

ActionChains執行原理 他是按照設計好的動作順序鏈式執行&#xff1b; 當調用ActionChains的方法時&#xff0c;不會立即執行&#xff0c;只是將要做的動作安裝順序存放在隊列中&#xff1b;當調用perform()方法時&#xff0c;隊列中的方法會依次執行&#xff1b; from sele…

西南科技大學數字電子技術實驗三(MSI邏輯器件設計組合邏輯電路及FPGA的實現)預習報告

一、計算/設計過程 說明:本實驗是驗證性實驗,計算預測驗證結果。是設計性實驗一定要從系統指標計算出元件參數過程,越詳細越好。用公式輸入法完成相關公式內容,不得貼手寫圖片。(注意:從抽象公式直接得出結果,不得分,頁數可根據內容調整) 1、4位奇偶校驗器 真值表 …

C++ Qt開發:使用關聯容器類

當我們談論編程中的數據結構時&#xff0c;順序容器是不可忽視的一個重要概念。順序容器是一種能夠按照元素添加的順序來存儲和檢索數據的數據結構。它們提供了簡單而直觀的方式來組織和管理數據&#xff0c;為程序員提供了靈活性和性能的平衡。 Qt 中提供了豐富的容器類&…

AI:大模型技術

Prompt Prompt&#xff08;提示&#xff09;是一種在人工智能領域&#xff0c;特別是在自然語言處理和聊天機器人中常用的技術。它是一種輸入&#xff0c;用于激發人工智能模型生成相應的輸出。在聊天機器人中&#xff0c;用戶輸入的問題或請求就是提示&#xff0c;而聊天機器…

基于AidLux的工業視覺少樣本缺陷檢測實戰應用

1. 模型轉換 AIMO網站&#xff1a; http://aimo.aidlux.com/ 試用賬號和密碼&#xff1a; 賬號&#xff1a;AIMOTC001 &#xff0c;密碼&#xff1a;AIMOTC001 上傳模型選擇目標平臺參數設置選擇自動轉換轉換結果并下載 2. 基于AidLux的語義分割模型部署 dataset2aidlux文件…

期待一下elasticsearch還未發布的8.12版本,由lucene底層帶來的大幅度提升

現在是北京時間23年12月10日。當前es最新版本還是es8.11版本。我們可以期待一下不久的將來&#xff0c;es的8.12版本看到大幅度的檢索性能提升。受益于 Lucene 9.9版本&#xff0c;內核帶來的大幅提升&#xff01; 此次向量檢索利用底層指令fma會性能提升5%。并且還提供了向量點…

在Spring Cloud使用Hystrix核心組件,并注冊到Eureka注冊中心去

其實吧&#xff0c;寫Spring Cloud系列&#xff0c;我有時候覺得也挺難受的&#xff0c;因為Spring Cloud的微服務啟動都需要一個一個來&#xff0c;并且在IDea中也需要占用比較大的內存&#xff0c;并且我本來可以一篇寫完5大核心組件的&#xff0c;但是我卻分了三篇&#xff…

簡單的圖像分類任務全流程示例(內含代碼)

以下是一個簡單的示例&#xff0c;展示了如何使用 PyTorch 處理自定義圖像分類數據集&#xff1a; import torch import torch.nn as nn import torch.optim as optim import torchvision import torchvision.transforms as transforms from torch.utils.data import DataLoad…

erlang實現用ets做一級緩存

一、Erlang中的ETS表和DETS表 ETS表是Erlang中的一種數據結構&#xff0c;它允許我們在內存中存儲數據。ETS表有許多用途&#xff0c;其中包括作為緩存的一種實現方式。ETS表的特點是它們在內存中以表的形式存儲數據&#xff0c;這使得訪問和操作數據非常快。 DETS表是Erlang…

【求職】外企德科-網易游戲測試面試記錄

前面的話&#xff1a;本來沒想寫&#xff0c;但是竟然收到了一面通過的通知&#xff0c;那就來回顧一下一面&#xff0c;為終面做做準備。 這次面試基本沒有做什么準備&#xff0c;本來也就是抱著試一試的心態做的筆試&#xff0c;結果筆試通過了&#xff0c;由于筆試的內容很…