C++语法|如何写出高效的C++代码(一)|对象使用过程中背后调用了哪些方法(构造和析构过程)?

文章目录

  • 再探拷贝构造函数和重载复制运算符
    • 实例化新对象和赋值操作
    • 强转为类类型
    • 指针和引用时临时对象的构造和析构过程
  • 考考你
    • 问题
    • 答案

再探拷贝构造函数和重载复制运算符

实例化新对象和赋值操作

首先我们写一个类,实现它的拷贝构造并重载赋值运算符。

class Test {
private:int ma;
public:Test(int a = 10): ma(a) {cout << "Test()构造函数" << endl;}~Test() {cout << "~Test析构函数" << endl;}Test(const Test& t) {cout << "Test const Test& t拷贝构造函数" << endl;}Test& operator=(const Test& t) {ma = t.ma;cout << "operator = 重载赋值运算符" << endl;return *this;}
};

然后我们构造类的对象,主要探究什么时候调用重载赋值运算符、什么时候调用拷贝构造

int main () {Test t1;Test t2(t1);Test t3 = t2;Test t4 = Test(20);cout << "-------------" << endl;return 0;
}

结果如下:

为什么没有调用我们的t3和t4没有调用重载赋值运算符呢?

首先t3调用的是拷贝构造函数,说明本质上这是一条构造语句,如果写成

Test t3;
t3 = t2; // 这里调用了重载的赋值运算符

再一个t4是直接调用构造函数,那是因为Test(20)显示生成临时对象,它的生命周期为所在语句,所以它被编译器优化了,实际上调用的是Test t4(20);,如果写成:

//t4.operator=(t2)
t4 = t2; //赋值,没有生成新对象
// t4.operator=(const Test &t)
t4 = Test(20); //这个临时对象会构造一个新对象,然后赋值给t4

此处的Test(20)会就会调用构造函数了,并且t4=会调用重载赋值构造函数,也就是说,只要我们不是在进行初始化对象,而是在赋值某个对象的话,就会调用重载赋值运算符。

强转为类类型

接着上面的代码

t4 = (Test)30; // int->Test

这样能转吗?其实是可以的,编译器会寻找Test类是否有一个合适的构造函数。显然我们这里是有的,因为我们的构造函数默认输入一个整形变量,所以编译器会为我们生成一个临时对象。甚至我们可以写成:这样的话我们是隐式生成临时对象。

t4 = 30;
//t4 = "aaa"; 报错,因为我们没有实现带char*类型的构造函数

指针和引用时临时对象的构造和析构过程

我们想考察一下指针和引用来获取临时对象:

Test *p = &Test(40);
//p指向的是一个已经析构的临时对象,该指针变为野指针
const Test &ref = Test(40);
//Test &ref = Test(30); 报错,必须是常引用

这里的指针甚至不能编译,这是由于较新的编译器代码检查机制更加严格,放以前肯定就编译通过了。并且该指针一定会成为野指针,因为出语句之后临时对象就会析构。并且较新的编译器const Test &ref = Test(40)也会报错,报错语句为:

error: taking the address of a temporary object of type 'Test' [-Waddress-of-temporary]

结论:在较新版本的编译器中,无论是用常引用还是指针,都不能指向一个临时对象

考考你

问题

对于以下类:

class Test {
public:// Test() Test(a) Test(b)Test(int a = 5, int b = 5) : ma(a), mb(b) {cout << "Test()构造函数" << endl;}~Test() {cout << "~Test()析构函数" << endl;}Test(const Test& src) : ma(src.ma), mb(src.mb) {cout << "Test(const Test&)拷贝构造函数" << endl;}Test& operator=(const Test& src) {ma = src.ma;mb = src.mb;cout << "operator=(const Test&)重载赋值运算符" << endl;return *this;}
private:int ma;int mb;
};

我们这样实例化这个类,请问每条语句都调用了类的哪些成员函数呢?

Test t1(10, 10);//1.Test(int, int)
int main() {Test t2(20, 20);//3.Test(int, int)Test t3 = t2; //4.Test(const Test&)/*静态变量和全局变量程序运行的时候内存就已经存在了,因为数据段内存是事先就分配好的但是静态局部变量只在第一次运行到它才会初始化,所以并不会构造先t4*///static Test t4(30,30);static Test t4 = Test(30, 30);//5.Test(int, int)t2 = Test(40, 40); //6.Test(int, int) operator= ~Test()//(50, 50) = 50 逗号表达式t2 = (Test)(50, 50);//7.(Test)50; Test(int, int) operator= ~Test()t2 = 60;	// Test(int) 8.Test(int, int) operator= ~Test()Test *p1 = new Test(70, 70); //9. Test(int, int)Test *p2 = new Test[2]; // 10. Test(int, int) Test(int, int)//较新的编译器无法通过编译Test *p3 = &Test(80, 80);  //11.Test(int, int) ~Test()const Test &p4 = Test(90, 90); //12.Test(int, int)delete p1; //13.~Test() 然后free内存delete[]p2; //14.~Test() ~Test() 然后free内存
}
Test t5(100, 100); //2. Test(int, int)

运行以后,这个类的构造、析构、拷贝构造的调用过程已经写在注释中了,并且最后的析构顺序为p1->p2->p4->t3->t2->t4->t5->t1 一共应该是9连析构

答案

你答对了吗?(这里的输出结果是注释Test *p3 = &Test(80, 80);后的结果)

在这里插入图片描述

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

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

相关文章

pycharm中导入rospy(ModuleNotFoundError: No module named ‘rospy‘)

1. ubuntu安装对应版本ros ubuntu20.04可参考&#xff1a; https://wiki.ros.org/cn/noetic/Installation/Ubuntuhttps://zhuanlan.zhihu.com/p/515361781 2. 安装python3-roslib sudo apt-get install python3-roslib3.在conda环境中安装rospy pip install rospkg pip in…

快速话术本(常用文本快速复制工具)EXE成品+软件源码

功能介绍 经常性需要重复性的输入几个不同的文本&#xff0c;来回复制很麻烦&#xff0c;这个小工具可以帮你解决&#xff0c;把要经常输入的文本添加进去&#xff0c;点击即可复制~ 链接&#xff1a;https://pan.baidu.com/s/1TqtJ_Xb10k4j3dxSRjz47Q?pwdsl4l 提取码&…

springMVC入门学习

目录 1、 什么是springmvc 2、springmvc工作流程 3、 springmvc快速入门&#xff08;XML版本&#xff09; 4、加载自定义目录下的springmvc.xml配置文件 5、 解析器InternalResourceViewResolver 6、 映射器BeanNameUrlHandlerMapping 7、 适配器SimpleControllerHandle…

【linux】进程概念|task_struct|getpid|getppid

目录 ​编辑 1.进程的概念 进程的基本概念 进程与程序的主要区别 进程的特征 进程的状态 描述进程—PCB task_struct中的内容 查看进程 1.创建一个进程&#xff0c;运行以下代码 通过系统调用获取进程标示符 getpid()以及getppid() 1.进程的概念 进程的基本概念…

RAG 检索的底座:Milvus Cloud向量数据库

在业界实践中,RAG 检索通常与向量数据库密切结合,也催生了基于 ChatGPT + Vector Database + Prompt 的 RAG 解决方案,简称为 CVP 技术栈。这一解决方案依赖于向量数据库高效检索相关信息以增强大型语言模型(LLMs),通过将 LLMs 生成的查询转换为向量,使得 RAG 系统能在向…

【Arduino IDE 2】Windows平台安装ESP8266 NodeMCU LittleFS Uploader(文件上传插件)

在Arduino IDE 2&#xff08;2.2.1或更高版本&#xff09;上&#xff0c;如何安装基于ESP8266 NodeMCU的LittleFS文件系统上传插件&#xff0c;以及如何将文件上传到ESP8266 NodeMCU板文件系统。 一、LittleFS简介 LittleFS是一个为微控制器创建的轻量级文件系统&#xff0c;可…

PostgreSQL和openGauss优化器对一个关联查询的SQL优化改写

PostgreSQL和openGauss数据库优化器在merge join关联查询的SQL优化改写 PostgreSQL 查询计划openGauss 查询计划拓展对比 看腻了文章就来听听视频讲解吧&#xff1a;https://www.bilibili.com/video/BV1oH4y137P7/ 数据库类型数据库版本PostgreSQL16.2openGauss6.0 创建测试表…

OpenNJet产品体验-手把手在Ubuntu20.04系统从零部署到应用OpenNJet

目录 一、引言 二、OpenNJet产品安装 2.1下载OpenNJet安装包 2.2安装OpenNJet V2.0.1 ​2.3快速启动并测试OpenNJet 三、OpenNJet产品应用体验 3.1配置OpenNJet 3.2 部署 Web 应用程序 3.3启动 NJet 3.4访问 Web 应用程序 四、总结 一、引言 OpenNJet应用引擎是高性…

2024年最适合做的母婴赛道,选品思路揭秘,教你如何选品!

大家好&#xff0c;我是电商花花。 在我印象中&#xff0c;每年都有人唱衰抖音小店不好做了&#xff0c;太卷了&#xff0c;普通人没有机会了&#xff0c;但是现在直播电商时代&#xff0c;很多信息都会片面的&#xff0c;做不好并不是因为不好做&#xff0c;而是因为你做不好…

力扣刷题--数组--第三天

今天再做两道二分查找的题目&#xff0c;关于二分查找的知识可看我前两篇博客。话不多说&#xff0c;直接开干&#xff01; 题目1&#xff1a;69.x 的平方根 题目详情&#xff1a;   给你一个非负整数 x &#xff0c;计算并返回 x 的 算术平方根 。由于返回类型是整数&#…

C++新特性-线程

主要内容 thread、condition、mutexatomicfunction、bind使用新特性实现线程池&#xff08;支持可变参数列表&#xff09;异常协程其他 1 C11多线程thread 重点&#xff1a; join和detach的使用场景thread构造函数参数绑定c函数绑定类函数线程封装基础类互斥锁mutexconditi…

【智能楼宇秘籍】一网关多协议无缝对接BACnet+OPC+MQTT

在繁华的都市中心&#xff0c;一座崭新的大型商业综合体拔地而起&#xff0c;集购物、餐饮、娱乐、办公于一体&#xff0c;是现代城市生活的缩影。然而&#xff0c;这座综合体的幕后英雄——一套高度集成的楼宇自动化系统&#xff0c;正是依靠多功能协议网关&#xff0c;实现了…