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.mzph.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;由于笔试的内容很…