多态下,构造函数和析构函数的顺序,以及父类、子类的转换

news/2025/9/27 12:02:30/文章来源:https://www.cnblogs.com/cmyg/p/19114932

 

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cstring>
  5 using namespace std;
  6 
  7 #define ll long long
  8 
  9 const int maxn=1e5+10;
 10 
 11 class A {
 12 public:
 13     A() {
 14         cout << "build A" << endl;
 15     }
 16     virtual ~A() {
 17         cout << "delete A" << endl;
 18     }
 19     virtual void work() {
 20         cout << "work A" << endl;
 21     }
 22 };
 23 
 24 class B: public A {
 25 public:
 26     B() {
 27         cout << "build B" << endl;
 28     }
 29     ~B() {
 30         cout << "delete B" << endl;
 31     }
 32     virtual void work() override {
 33         cout << "work B" << endl;
 34     }
 35 };
 36 
 37 class C {
 38 public:
 39     C() {
 40         cout << "build C" << endl;
 41     }
 42     virtual ~C() {
 43         cout << "delete C" << endl;
 44     }
 45     virtual void work() {
 46         cout << "work C" << endl;
 47     }
 48 };
 49 
 50 class D: public C {
 51 public:
 52     D() {
 53         cout << "build D" << endl;
 54     }
 55     ~D() {
 56         cout << "delete D" << endl;
 57     }
 58     virtual void work() override {
 59         cout << "work D" << endl;
 60     }
 61     void init() {
 62         memset(a, 0, sizeof(a));
 63     }
 64 private:
 65     int a[1000];
 66 };
 67 
 68 int main()
 69 {
 70     cout << "test1:" << endl;
 71     A * obj1 = new B();
 72     obj1 -> work();
 73     delete obj1;
 74     obj1 = nullptr;
 75     cout << endl;
 76 /*
 77 build A
 78 build B
 79 work B
 80 delete B
 81 delete A
 82 */
 83 
 84 /*
 85 如果class A析构函数不写成虚函数
 86 build A
 87 build B
 88 work B
 89 delete A
 90 */
 91 
 92     cout << "test2:" << endl;
 93     A * obj2 = (A*)new B();
 94     obj2 -> work();
 95     delete obj2;
 96     obj2 = nullptr;
 97     cout << endl;
 98 //和上面一样的结果
 99 
100     //wrong 编译的时候
101     /*
102     B * obj3 = new A();
103     obj3 -> work();
104     delete obj3;
105     obj3 = nullptr;
106     */
107 
108     cout << "test3:" << endl;
109     B * obj4 = (B*)new A();
110     obj4 -> work();
111     delete obj4;
112     obj4 = nullptr;
113     cout << endl;
114 /*
115 build A
116 work A
117 delete A
118 */
119 
120     cout << "test4:" << endl;
121     C * obj5 = new D();
122     obj5 -> work();
123     delete obj5;
124     obj5 = nullptr;
125     cout << endl;
126 /*
127 build C
128 build D
129 work D
130 delete D
131 delete C
132 */
133 
134     cout << "test5:" << endl;
135     D * obj6 = (D*)new C();
136     obj6 -> work();
137     //obj6 -> init(); //运行时报错
138     delete obj6;
139     obj6 = nullptr;
140     cout << endl;
141 /*
142 build C
143 work C
144 delete C
145 */
146 
147     return 0;
148 }
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cstring>
  5 using namespace std;
  6 
  7 #define ll long long
  8 
  9 const int maxn=1e5+10;
 10 
 11 class A {
 12 public:
 13     A() {
 14         cout << "build A" << endl;
 15     }
 16     virtual ~A() {
 17         cout << "delete A" << endl;
 18     }
 19     virtual void work() {
 20         cout << "work A" << endl;
 21     }
 22 };
 23 
 24 class B: public A {
 25 public:
 26     B() {
 27         cout << "build B" << endl;
 28     }
 29     ~B() {
 30         cout << "delete B" << endl;
 31     }
 32     virtual void work() override {
 33         cout << "work B" << endl;
 34     }
 35 };
 36 
 37 class C {
 38 public:
 39     C() {
 40         cout << "build C" << endl;
 41     }
 42     virtual ~C() {
 43         cout << "delete C" << endl;
 44     }
 45     virtual void work() {
 46         cout << "work C" << endl;
 47     }
 48 };
 49 
 50 class D: public C {
 51 public:
 52     D() {
 53         cout << "build D" << endl;
 54     }
 55     ~D() {
 56         cout << "delete D" << endl;
 57     }
 58     virtual void work() override {
 59         cout << "work D" << endl;
 60     }
 61     void init() {
 62         memset(a, 0, sizeof(a));
 63     }
 64 private:
 65     int a[1000];
 66 };
 67 
 68 int main()
 69 {
 70     cout << "test1:" << endl;
 71     A * obj1 = new B();
 72     obj1 -> work();
 73     delete obj1;
 74     obj1 = nullptr;
 75     cout << endl;
 76 /*
 77 build A
 78 build B
 79 work B
 80 delete B
 81 delete A
 82 */
 83 
 84 /*
 85 如果class A析构函数不写成虚函数
 86 build A
 87 build B
 88 work B
 89 delete A
 90 */
 91 
 92     cout << "test2:" << endl;
 93     A * obj2 = (A*)new B();
 94     obj2 -> work();
 95     delete obj2;
 96     obj2 = nullptr;
 97     cout << endl;
 98 //和上面一样的结果
 99 
100     //wrong 编译的时候
101     /*
102     B * obj3 = new A();
103     obj3 -> work();
104     delete obj3;
105     obj3 = nullptr;
106     */
107 
108     cout << "test3:" << endl;
109     B * obj4 = (B*)new A();
110     obj4 -> work();
111     delete obj4;
112     obj4 = nullptr;
113     cout << endl;
114 /*
115 build A
116 work A
117 delete A
118 */
119 
120     cout << "test4:" << endl;
121     C * obj5 = new D();
122     obj5 -> work();
123     delete obj5;
124     obj5 = nullptr;
125     cout << endl;
126 /*
127 build C
128 build D
129 work D
130 delete D
131 delete C
132 */
133 
134     cout << "test5:" << endl;
135     D * obj6 = (D*)new C();
136     obj6 -> work();
137     //obj6 -> init(); //运行时报错
138     delete obj6;
139     obj6 = nullptr;
140     cout << endl;
141 /*
142 build C
143 work C
144 delete C
145 */
146 
147     return 0;
148 }

 

这段代码主要展示了 C++ 中类的继承、多态、虚函数以及对象的创建与销毁过程,我们来逐一分析各个测试用例的输出结果:

test1 分析

cpp
 
运行
 
 
 
 
A * obj1 = new B();
obj1 -> work();
delete obj1;
 
  • 当创建B对象时,会先调用父类A的构造函数,再调用B自身的构造函数,因此输出build Abuild B
  • work()是虚函数,通过基类指针调用时会执行实际指向对象(B)的work()方法,因此输出work B
  • A的析构函数是虚函数,删除基类指针时会先调用派生类B的析构函数,再调用基类A的析构函数,因此输出delete Bdelete A
如果A的析构函数不是虚函数,删除基类指针时只会调用基类A的析构函数,导致B的析构函数不会被执行,这就是为什么需要将基类析构函数声明为虚函数的原因。

test2 分析

cpp
 
运行
 
 
 
 
A * obj2 = (A*)new B();
 
这里的强制类型转换是多余的,因为BA的派生类,B*可以隐式转换为A*。所以这段代码的行为和 test1 完全相同,输出结果也一样。

test3 分析

cpp
 
运行
 
 
 
 
B * obj4 = (B*)new A();
 
  • 这里创建的是A对象,所以只调用A的构造函数:build A
  • 虽然通过强制类型转换将A*转为B*,但实际对象还是A,所以work()调用的是A的版本:work A
  • 删除时,由于实际对象是A,所以只调用A的析构函数:delete A
这种转换是不安全的,因为A对象不包含B的成员,可能会导致未定义行为。

test4 分析

cpp
 
运行
 
 
 
 
C * obj5 = new D();
 
  • 类似 test1,创建D对象时先调用C的构造函数,再调用D的构造函数:build Cbuild D
  • work()是虚函数,调用的是D的版本:work D
  • C的析构函数是虚函数,删除时先调用D的析构函数,再调用C的析构函数:delete Ddelete C

test5 分析

cpp
 
运行
 
 
 
 
D * obj6 = (D*)new C();
 
  • 这里创建的是C对象,所以只调用C的构造函数:build C
  • 实际对象是C,所以work()调用的是C的版本:work C
  • 删除时,实际对象是C,所以只调用C的析构函数:delete C
同样,这种将基类指针强制转换为派生类指针的做法是不安全的。

总结关键知识点:

  1. 派生类对象创建时,先调用基类构造函数,再调用派生类构造函数
  2. 虚函数实现多态:基类指针指向派生类对象时,调用的是派生类的虚函数实现
  3. 基类析构函数应声明为虚函数,以确保删除基类指针时能正确调用派生类析构函数
  4. 向下转型(基类指针转派生类指针)是不安全的,可能导致未定义行为
这些测试用例很好地展示了 C++ 面向对象编程中的多态性和对象生命周期管理的重要概念。

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

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

相关文章

US$49 B48 amp; MSV90 ISN Reading via OBD Authorization for Yanhua Mini ACDP

Software License for YANHUA ACDP B48 Integrated Interface BoardWhen you buy Yanhua DME B48 Integrated Interface Board (SK247-F3), you will need to purchase this software license as well.No need shippin…

泰安微网站建设凡客诚品线下店

From: http://blog.csdn.net/wangfeng2500/article/details/7650062 在TCP层&#xff0c;有个FLAGS字段&#xff0c;这个字段有以下几个标识&#xff1a;SYN, FIN, ACK, PSH, RST, URG. 其中&#xff0c;对于我们日常的分析有用的就是前面的五个字段。 它们的含义是&#x…

在CodeBolcks下wxSmith的C++编程教程——使用 wxGrid

0.前言 欢迎来到 wxSmith 教程页面!wxSmith 与 Code::Blocks、wxWidgets 和 C++ 编译器相结合,为您提供一种所见即所得的方式来创建具有图形用户界面 (GUI) 的应用程序。该组合形成了一个用于快速应用程序开发 (R…

题解:P12479 [集训队互测 2024] 长野原龙势流星群

题目: 唉不是,这个 trick 我见过啊 QAQ! 我们想一下特殊点,发现最大的点肯定选自己,然后又会发现他的父亲也必选他,所以每次找最大的点和他父亲合并成新点即可。 合并了贪心选点的过程。 #include<bits/stdc+…

详细介绍:Docker(一)—— Docker入门到精通:从基础概念到容器管理

详细介绍:Docker(一)—— Docker入门到精通:从基础概念到容器管理2025-09-27 11:55 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto…

linux下nginx

sudo apt install nginx sudo systemctl start nginx # 设置开机自启 sudo systemctl enable nginx配置文件 MacOS (Homebrew 安装): /usr/local/etc/nginx/nginx.conf Linux: /etc/nginx/nginx.conf Debian/Ubuntu 系…

青岛网站制作辰星辰凤岗本地网站

隐藏方法&#xff1a;1、打开apache的http.conf配置文件&#xff0c;开启mod_rewrite.so模块&#xff1b;2、AllowOverride None项中将None改为All&#xff1b;3、修改“.htaccess”的配置内容&#xff0c;将原代码替换为官方手册提供的代码。thinkphp现在的php主流框架之一&am…

网站建设结构总结网站建站和维护

需求&#xff1a; 上传文件&#xff0c;但是后端接口不支持多文件上传&#xff0c;但是一次性发出很多请求的话如果有100个文件那对后端的压力又太大了在上传的时候还需要有停止上传的按钮 进程&#xff1a; async await 只能做到第一步&#xff0c;但是无法在上传中的时候关…

OI 笑传 #12

Megaakuaiiro这次是 ABC424 423 的 DEF。 ABC424D 朴素状压即可。 codeShow me the code #define psb push_back #define mkp make_pair #define ls p<<1 #define rs (p<<1)+1 #define rep(i,a,b) for( in…

spatial芯片设计语言 学习笔记

Spatial 芯片设计语言是斯坦福 PPL 研究组在特定领域(高级硬件/加速器设计)的研究成果和实践平台。 斯坦福 PPL 是斯坦福计算机科学系下的一个研究实验室,全称是 Pervasive Parallelism Laboratory 普及并行实验室。…

非诚勿扰 —— 大龄单身男,找人生合伙人,有意者邮件联系

非诚勿扰 —— 大龄单身男,找人生合伙人,有意者邮件联系89年辽宁人,单身,目前在八达岭长城脚下工厂务工,无车无房无存款,工作也不算太稳定。为人执拗,真诚,社会经验及阅历极少,赚钱能力极差,诚招人生合伙人(…

soul 这款APP太差劲了,天天都有婚介加我,怎么个事情,还能不能好好的解决解决个人问题了

soul 这款APP太差劲了,天天都有婚介加我,怎么个事情,还能不能好好的解决解决个人问题了本博客是博主个人学习时的一些记录,不保证是为原创,个别文章加入了转载的源地址,还有个别文章是汇总网上多份资料所成,在这…

【C++】23. C++11(上) - 教程

【C++】23. C++11(上) - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&quo…

kali2025搭建ARL灯塔系统

ARL灯塔详细安装教程ARL灯塔详细安装教程 安装配置docker apt-get update apt install docker.io apt install docker-compose如果出现以下报错请再运行一遍,或者更换镜像源然后还需要配置国内的docker加速,可以网上…

局域网内的网站建设优化营商环境工作开展情况汇报

安装selenium 打开命令控制符输入&#xff1a;pip install -U selenium 火狐浏览器安装firebug&#xff1a;www.firebug.com&#xff0c;调试所有网站语言&#xff0c;调试功能 Selenium IDE 是嵌入到Firefox 浏览器中的一个插件&#xff0c;实现简单的浏览器操 作的录制与回…

单片机 -- USART总线 - 实践

单片机 -- USART总线 - 实践2025-09-27 11:41 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important;…

实用指南:AI 术语通俗词典:LLM(大语言模型)

实用指南:AI 术语通俗词典:LLM(大语言模型)pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &…

平面电商网站建设东莞企业营销型网站策划

一&#xff0e;代码规范和建议 避免Update LateUpdate等函数内频繁的GC Alloc&#xff0c;避免在Update和LateUpdate内有以下操作&#xff1a; 调用GetComponet() 调用FindObjectsOfType() 使用GameObject.Tag和GameObject.Name 等等其他有堆内存分配的操作 避免频繁调用T…

一个空间可以放两个网站吗简历怎么制作网站

GPADC 模块介绍 GPADC 是 12bit 采集精度的模数转换模块&#xff0c;支持 4 路通道&#xff0c;模拟输入范围 0-1.8v&#xff0c;最高采样率 1MHZ&#xff0c;并且支持数据比较&#xff0c;自校验功能&#xff0c;同时工作于可配置的四种工作模式&#xff1a; Single mode&a…

java学习 2025-9-27

📅 学习日志 2025-9-27 Java注释单行注释:// comment多行注释:/* * comment1 * comment2 */文档注释:/** * comment1 * comment2 */阿弥陀佛:数据类型String:(似乎不是数据类型而是类,类似还有Byte等)大小写…