55 - 经典问题解析四(动态内存分配虚函数继承中的强制类型转换)

news/2024/5/7 18:54:42/文章来源:https://blog.csdn.net/weixin_36098975/article/details/128079641

---- 整理自狄泰软件唐佐林老师课程

1. 关于动态内存分配

new和malloc的区别是什么?
delete和free的区别又是什么?

1.1 问题一:new和malloc的区别

1.1.1 new关键字和malloc函数的区别

new关键字是C++的一部分malloc是由C库函数提供的函数
new以具体 类型 为单位进行内存分配malloc以 字节 为单位进行内存分配
new在申请内存空间时可进行初始化malloc仅根据需要申请定量的内存空间
new在所有C++编译器都被支持malloc在某些系统开发中是不能调用的
new能够触发构造函数的调用malloc仅分配需要的内存空间
对象的创建只能使用newmalloc不适合面向对象开发

1.1.2 编程实验:new和malloc的区别一

下面的代码输出什么?为什么?

在这里插入图片描述

1.1.3 编程实验:new和malloc的区别二

下面的代码输出什么?为什么?

在这里插入图片描述

1.2 问题二:delete和free的区别

delete在所有C++编译器中都被支持free在某些系统开发中是不能调用的
delete能够触发析构函数的调用free仅归还之前分配的内存空间
对象的销毁只能使用deletefree不适合面向对象开发

2. 关于虚函数

2.1 问题一

构造函数是否可以成为虚函数?
析构函数是否可以成为虚函数?

  • 构造函数 不可能 成为虚函数

    在构造函数执行结束后,虚函数表指针才会被正确的初始化

  • 析构函数 可以 成为虚函数

    建议在设计类时将析构函数声明为虚函数

  • 编程实验:构造、析构、虚函数

#include <iostream>
#include <string>using namespace std;class Base
{
public:Base(){cout << "Base()" << endl;func();}virtual void func() {cout << "Base::func()" << endl;}virtual ~Base(){func();cout << "~Base()" << endl;}
};class Derived : public Base
{
public:Derived(){cout << "Derived()" << endl;func();}virtual void func(){cout << "Derived::func()" << endl;}~Derived(){func();cout << "~Derived()" << endl;}
};int main()
{Base* p = new Derived();// ...delete p;return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2 问题二

构造函数中是否可以发生多态?
析构函数中是否可以发生多态?

  • 构造函数中 不可能 发生多态行为

    在构造函数执行时,虚函数表指针 未被 正确初始化

  • 析构函数中 不可能 发生多态行为

    在析构函数执行时,虚函数表指针 已经被销毁

  • 构造函数和析构函数中调用虚函数不可能发生多态行为,只调用 当前类中 定义的函数版本

在这里插入图片描述

3. 关于继承中的强制类型转换

  • 继承中如何正确的使用强制类型转换?

    • dynamic_cast是与继承相关的类型转换关键字

    • dynamic_cast要求相关的类中必须有虚函数

    • 用于直接或者间接继承关系的指针(引用)之间

      • 指针:
        转换成功:得到目标类型的指针
        转换失败:得到一个 空指针

      • 引用:
        转换成功:得到目标类型的引用
        转换失败:得到一个 异常操作信息

    • 编译器会检查dynamic_cast的使用是否正确

    • 类型转换的结果只可能在 运行阶段 才能得到

  • 编程实验:dynamic_cast的使用

在这里插入图片描述
注解:

#include <iostream>
using namespace std;class A {
public:virtual ~A() { cout<<"~A()"<<endl; }
};class B : public A{
public:virtual ~B() { cout<<"~B()"<<endl; }
};class C : public B{
public:virtual ~C() { cout<<"~C()"<<endl; }
};class D :public C {
public:virtual ~D() { cout<<"~D()"<<endl; }
};int main(){A* pA;B* pB;C* pC;D* pD = new D;pA = dynamic_cast<A*>(pD);  //向上转型成功if (pA == NULL){cout<<"Upcasting failed: D* to A*"<<endl;} else {cout<<"Upcasting successfully: D* to A*"<<endl;}pB = dynamic_cast<B*>(pD);  //向上转型成功if (pB == NULL){cout<<"Upcasting failed: D* to B*"<<endl;} else {cout<<"Upcasting successfully: D* to B*"<<endl;}pC = dynamic_cast<C*>(pD);  //向上转型成功if (pC == NULL){cout<<"Upcasting failed: D* to C*"<<endl;} else {cout<<"Upcasting successfully: D* to C*"<<endl;}cout << endl;delete pD;return 0;
}

在这里插入图片描述

#include <iostream>
using namespace std;class A {
public:virtual ~A() { cout<<"~A()"<<endl; }
};class B : public A{
public:virtual ~B() { cout<<"~B()"<<endl; }
};class C : public B{
public:virtual ~C() { cout<<"~C()"<<endl; }
};class D :public C {
public:virtual ~D() { cout<<"~D()"<<endl; }
};int main(){A* pA = new A;B* pB;C* pC;D* pD;pB = dynamic_cast<B*>(pA);  //向下转型失败if (pB == NULL){cout<<"Downcasting failed: A* to B*"<<endl;} else {cout<<"Downcasting successfully: A* to B*"<<endl;}pC = dynamic_cast<C*>(pA);  //向下转型失败if (pC == NULL){cout<<"Downcasting failed: A* to C*"<<endl;} else {cout<<"Downcasting successfully: A* to C*"<<endl;}pD = dynamic_cast<D*>(pA);  //向下转型失败if (pD == NULL){cout<<"Downcasting failed: A* to D*"<<endl;} else {cout<<"Downcasting successfully: A* to D*"<<endl;}cout << endl;delete pA;return 0;
}

在这里插入图片描述

  • 上述代码中,类的继承顺序是 A==>B==>C==>D,
    • 当pA是指向A类型的对象时( pA = new A; ),向下转型失败,pA不能转换为B*、C*、D*类型。
    • 当pD是指向D类型的对象时( pD = new D; ),向上转型成功,pD可以转换为A*、B*、D*类型。
    • 原因:因为每个类都会在内存中保存一份类型信息,编译器会将存在继承关系的类的类型信息使用指针“连接”起来,从而形成一个继承链,如下所示。
        当使用 dynamic_cast 对指针进行类型转换时,程序会先找到该指针指向的对象,再根据对象找到当前类(指针指向的对象所属的类)的类型信息,并从此节点开始沿着继承链向上遍历,如果找到了要转化的目标类型,那么说明这种转换是安全的,就能够转换成功,如果没有找到要转换的目标类型,那么说明这种转换存在较大的风险,就不能转换。
      在这里插入图片描述

4. 小结

  • new / delete会触发构造函数或者析构函数的调用
  • 构造函数不能成为虚函数
  • 析构函数可以成为虚函数
  • 构造函数和析构函数中都无法产生多态行为
  • dynamic_cast是与继承相关的专用关键字

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_227017.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

40 行 Python 代码,写一个 CPU

一、引言 CPU 如何工作&#xff1f;是困扰初级用户一个迷雾般的难题。我们可能知道诸如程序计数器、RAM、寄存器的只言片语&#xff0c;但尚未对这些部件的工作原理及整个系统的协同有清晰和总体的认识。 本文使用四十行 Python 代码来实现一个最简单的 CPU。使它可编程&…

视频怎么转换为音频文件?快来掌握这几种方式

大家平时在下载网课资源进行学习的时候&#xff0c;看久了眼睛也会开始疲劳&#xff0c;而且有些视频的画面看起来很枯燥。其实我们可以使用一些软件把视频中的音频分离出来&#xff0c;直接收听音频也可以学到知识&#xff0c;而且我们还可以处理其他的事情&#xff0c;是不是…

android接入微信API相关细节

细节1 想要接入微信&#xff0c;如接入分享微信功能、跳转小程序功能等&#xff1b;首先需要到微信开放平台申请AppId&#xff0c;如何申请在开放平台上的流程很清楚&#xff0c;就不赘述了 但有个细节就是应用包名签名&#xff0c;这个应用包名签名记得是以app有正式签名文件…

sklearn机器学习:决策树案例

系列文章目录 文章目录系列文章目录一、决策树原理1.1 定义1.2 优点1.3 缺点二、分类树2.1 函数语法2.2 案例三、回归树案例3.1 函数语法3.2 案例一、决策树原理 1.1 定义 决策树是一种用来 classification &#xff08;分类&#xff09;和 regression&#xff08;回归&#…

【树莓派不吃灰】Linux篇⑩ 学习例行性工作排程(核心概念)

目录1. 什么是例行性工作排程2. 仅运行一次的工作排程3. 循环运行的例行性工作排程4. 可唤醒停机期间的工作任务5. 重点回顾❤️ 博客主页 单片机菜鸟哥&#xff0c;一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2022-11-28 ❤️❤️ 本篇更新记录 2022-11-28 ❤️&…

python>>numpy(第二讲)

章节内容 元素操作 常用的方法 广播 数组形状操作 排序数组 目录 元素操作 一些常用的方法 广播 数组形状操作 数组排序 元素操作 生成元素a np.array([1,2,3,4]) b np.ones(4)1 生成一个原来数组的n倍生成一个所有元素均跟2次方有关的数组一个计算矩阵相乘的函数判断两个…

2022王道OS 1.2 操作系统的发展与分类

2022 王道OS 操作系统的发展与分类 文章目录2022 王道OS 操作系统的发展与分类知识总览OS的发展与分类手工操作阶段批处理阶段--单道批处理系统批处理阶段--多道批处理系统分时操作系统实时操作系统其他几种OS知识回顾与重要考点文章目录2022 王道OS 操作系统的发展与分类知识总…

jsp美食管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 美食管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统采用serlvet dao bean mvc模式开发&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式 开发。开发环境为TOMCAT7.0,Myeclipse8…

QT-MySQL QSqlDatabase: QMYSQL driver not loaded

使用Qt连接mysql数据库&#xff0c;遇到了一个问题&#xff0c;就是QT5.14.1版本在连接MySQL数据库时候&#xff0c;提示驱动加载失败&#xff0c;“QMYSQL driver not loaded” 编程环境&#xff1a;   Qt5.14.1,编译器是MSVC2017_64,是64位的。   mysql-8.0.30-winx64&am…

如何在 docker 容器使用 nginx 实现反向代理统一站点入口

在微服务架构下&#xff0c;我们会部署很多微服务来实现我们的系统。每个微服务会有不同的端口。而用户在访问我们的站点时希望通过统一的端口来访问所有的服务&#xff0c;因为在很多情况下用户只能通过 80 或者 443 端口访问外界服务。 这个时候我们就可以使用反向代理来实现…

Kubernetes集群coredns缓存容器bind: address already in use错误导致集群服务无法互通解决

coredns缓存nodelocal dns cache :53: bind: address already in use错误处理起因分析问题处理问题重启Node local dns起因 事情起因是Kubernetes集群内的服务无法互相访问了 分析问题 因为Kubernetes集群内的服务都是通过service、pod的名称作为域名到coredns解析Cluster I…

[附源码]计算机毕业设计springboot贷款申请审核管理系统论文

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【简单、高效、性能好】SetFit:无需Prompts的高效小样本学习

重磅推荐专栏&#xff1a; 《Transformers自然语言处理系列教程》 手把手带你深入实践Transformers&#xff0c;轻松构建属于自己的NLP智能应用&#xff01; 1. 概要 使用预训练语言模型的小样本学习&#xff08;处理只有少量标签或没有标签的数据&#xff09;已成为比较普遍的…

Zlibrary已死,找了一个替代品,找了一个替代品免费的电子书下载平台...

大家好&#xff0c;我是鸟哥。一个半路出家的程序员。 提到Zlibrary&#xff0c;想必大家都不陌生吧。全球最大的数字图书馆&#xff0c;截止被封前共收录了591万本书&#xff0c;7751万篇文章&#xff0c;并且还在不断的增加中&#xff0c;关键是可以免费下载。 反正我是很熟悉…

智能计量系统配套设备有哪些

智能计量系统配套设备 地磅区域安装配套设备包含&#xff1a;微波定位仪、视频监控、道闸、LED显示屏、车号识别、语音对讲、音响设备、红绿灯、刷卡机箱、雷达、补光灯。 硬件设备 1、微波定位仪&#xff1a;通过微波定位仪设备&#xff0c;可以判断车辆是否完全上磅。 2、…

C++11(一)

&#x1f9f8;&#x1f9f8;&#x1f9f8;各位大佬大家好&#xff0c;我是猪皮兄弟&#x1f9f8;&#x1f9f8;&#x1f9f8; 文章目录一、列表初始化initializer_list二、声明1.auto2.decltype3.nullptr三、C11 STL中的变化1.array2.forward_list3.STL其他变化四、C关键字新功…

【三维重建补充知识-0】视差、深度概念及其转换

一、基本概念 把手指放在眼前&#xff0c;分别闭上左、右眼&#xff0c;我们会发现手指与后边物体的相对位置是不同的&#xff0c;也即两眼所识别的两幅图像之间存在视觉差异&#xff0c;我们通过“视差”这一概念来表示这种差别。 该过程也可以通过两个处于同一平面的相机来模…

C++ 之 移动构造函数

1、左值和右值 C( 包括 C) 中所有的表达式和变量要么是左值&#xff0c;要么是右值。 通俗的左值的定义就是非临时对象&#xff0c;那些可以在多条语句中使用的对象&#xff0c;表达式结束后依然存在的持久化对象&#xff0c;所有的具名变量或者对象都是左值。右值是指临时的…

设置渐变边框色

如上图所示&#xff0c;需设置渐变边框色&#xff0c;左右边框颜色固定&#xff0c;上边框从左到右开始渐变&#xff0c;下边框从右到左开始渐变。 思考了很久&#xff0c;如果看作是一个div&#xff0c;则需要用到 border-image属性设置渐变色。也可以看作是两个div&#xff0…

CISAW信息安全保障人员认证考试难吗?

CISAW信息安全保障人员认证&#xff0c;作为信息安全行业相当热门的证书之一&#xff0c;其持证人数已超50%&#xff0c;在信息安全行业内占有一席之地&#xff0c;很多报考人都比较关心CISAW考试难不难&#xff1f;能通过吗&#xff1f;那接下来说一说CISAW证书考不好考&#…