⌈C++⌋深度剖析构造、拷贝构造与赋值运算符重载——深浅拷贝、explicit、类型转换等

目录

一、认识拷贝构造函数

1、什么是拷贝构造

2、深拷贝与浅拷贝

3、编译器可以绕过拷贝构造函数(C++ Primer P442)

4、explicit修饰

二、认识赋值运算符重载

1、赋值运算符重载格式

2、默认赋值运算符重载

3、赋值运算符都必须定义为成员函数

三、现代版拷贝构造与赋值运算符重载写法分析


一、认识拷贝构造函数

1、什么是拷贝构造

①当用一个已存在的对象创建一个新对象时,②当函数参数类型为类类型对象时,③当函数返回值类型为类类型对象时,编译器会自动调用拷贝构造函数;

(如下代码为,一个模拟string类的构造函数与拷贝构造⬇️)

namespace test {class string {public://构造函数string(const char* str = ""){_size = strlen(str);_capacity = _size;_str = new char[_size + 1];memcpy(_str, str, _size + 1);}//拷贝构造string(const string& s) {   //考虑为什么要加 const 与 引用_str = new char[s._capacity + 1];memcpy(_str, s._str, s._size + 1);_size = s._size;_capacity = s._capacity;}//析构函数~string() {delete[] _str;_str = nullptr;_size = _capacity = 0;}private:size_t _capacity;size_t _size;char* _str;};
}
int main() {test::string s1("for_test");  //调用构造函数test::string s2(s1);   //调用拷贝构造return 0;
}

🕛 拷贝构造传入单个形参(const string& s),该形参是改类类型的对象的引用,函数传参时如果不传入引用,会发生对象的拷贝(拷贝函数参数压入函数栈帧中),拷贝对象时又会再次调用拷贝构造,不断重复此步骤,引发无穷递归,导致栈溢出。

🕑const:由于是传入对象的引用,所以加上const,保证改对象在函数中不被修改;

2、深拷贝与浅拷贝

拷贝构造函数是构造函数的一个重载,与构造函数一样,如果不显式定义,编译器会自动生成默认的拷贝构造函数,默认的拷贝构造将内置类型的成员变量按照内存的字节序进行拷贝,而自定义类型会调用他自己的拷贝构造

那当我们不显示定义拷贝构造函数会出什么问题呢?⬇️

1️⃣成员变量所指向的地址空间相同:

无显示定义拷贝构造时(浅拷贝):

显示定义拷贝构造时:

2️⃣调用多次析构函数,对同一块空间进行多次释放:

如下方动画所示,当类中没有显式定义拷贝构造时,两个对象(s1, s2)的成员_str指向同一块空间,函数结束时,分别调用s2与s1的析构函数,导致_str所指向的空间被释放两次;

3、编译器可以绕过拷贝构造函数(C++ Primer P442)

在拷贝初始化过程中,编译器可以跳过拷贝/移动构造函数,直接创建对象,即:

	/*编译器被允许将第一行代码改写为第三行代码*/test::string s3 = "abcd";    //隐式调用拷贝构造test::string s4("abcd");     //编译器略过拷贝构造

两者汇编代码完全相同 

4、explicit修饰

上述第三点中的第一行代码,实质是将“abcd”实例化一个对象,再调用拷贝构造;

对于单个参数或者出第一个参数无默认值其余均有默认值的构造函数,具有上述类似的类型转换的作用;

当用explicit修饰构造函数后,该类型转换会被禁止!

1️⃣我们以上述代码为例:

2️⃣同样的例子我们还可以从vector的源代码中找到:

在vs编译阶段报错: 


二、认识赋值运算符重载

/**************以下为模拟实现string类的赋值运算符重载************/    string& operator=(const string& s) {if (this == &s) return *this;   //判断是否自己给自己赋值char* tmp = new char[s._capacity + 1];     //注意存放'\0'的空间memcpy(tmp, s._str, s._capacity + 1);delete[] _str;    //释放之前的空间_str = tmp;_size = s._size;_capacity = s._capacity;return *this;   //返回左值引用}

1、赋值运算符重载格式

参数类型:const T&, 传入引用提高传参效率

②返回值类型: T&, 返回一个指向左侧运算对象的引用(*this), 为了与内置类型的赋值运算符保持一致,即可连续赋值

③检查是否自己给自己赋值

2、默认赋值运算符重载

与处理拷贝构造函数一样,如果一个类未定义自己的拷贝赋值运算符,编译器会为他生成一个合成拷贝赋值运算符(默认),以值的方式逐字节拷贝,同时也要注意深浅拷贝问题;

3、赋值运算符都必须定义为成员函数

我们可以重载赋值运算符。不论形参的类型是什么,赋值运算符都必须定义为成员函数,因为如果用户在类外自己实现一个全局的赋值运算符,就和编译器在类中生成的默认赋值运算符重载冲突了


三、现代版拷贝构造与赋值运算符重载写法分析

//传参为类的引用的拷贝构造
string(const string& s) :_str(nullptr) {string tmp(s._str);   //调用传参为字符串的拷贝构造std::swap(tmp._str, _str);std::swap(tmp._size, _size);std::swap(tmp._capacity, _capacity);
}string& operator=(string tmp) {   //调用拷贝构造 string tmp(s);std::swap(tmp._str, _str);std::swap(tmp._size, _size);std::swap(tmp._capacity, _capacity);return *this;//函数结束tmp对象销毁
}

参考文献:《C++ Primer》

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

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

相关文章

【简单认识LVS+Keepalived负载均衡高可用群集】

文章目录 一、Keepalived高可用详解1、简介2、原理3、Keepalived体系主要模块及其作用: 二、LVSKeepalived 高可用群集部署实例1.配置NFS共享存储器2.配置节点web服务(两台的配置相同)(1)配置虚拟 IP 地址(…

Java中Map中10w条数据用什么循环性能最好呢?

加油,新时代打工人! 1、java中List集合三种获取集合元素方式 2、Java中Map使用增强for循环和迭代器获取key和value 选择合适的循环方式,让性能最优! public class Test2 {public static void main(String[] args) {//初始化 10w…

如何获取铁粉

忽然发现我的铁粉从100变成了540,分享下我的经验,我觉得可能是我的机器人经常互动的问题,结合自己的看法和平台大佬的想法一些进行了梳理: 在当今社交媒体时代,吸引和保留铁粉(忠实粉丝)对于个…

面试题更新之-DOCTYPE html相关问题

文章目录 <!DOCTYPE html>是什么&#xff1f;为什么要在html文件开头加上一个<!DOCTYPE html>DOCTYPE的作用&#xff0c;严格与混杂模式的区别&#xff0c;有何意义HTML5为什么只需要写<!DOCTYPE HTML> 是什么&#xff1f; 是HTML文档的文档类型声明&#xf…

实战攻防之积极防御体系建设 | 中睿天下受邀参与诸子云沙龙

7月8日&#xff0c;中睿天下受邀参与由诸子云举办的“网络与数据安全”主题沙龙&#xff0c;中睿天下技术经理徐丹丹就《实战攻防之积极防御体系建设》这一主题进行了分享交流。 本次沙龙由南京分会会长宋士明主持&#xff0c;活动邀请到BASF、江苏省联社、华泰证券、宁证期货、…

【kubernetes系列】Kubernetes之ServiceAccount

概述 Service Account是什么呢&#xff0c;顾名思义&#xff0c;服务账号&#xff0c;一种给服务使用的账号&#xff0c;它不是给Kubernetes的集群的用户&#xff08;系统管理员、运维人员、租户用户等&#xff09;使用&#xff0c;而是给运行在Pod里的进程用的&#xff0c;它…

flink启动报错Failed to construct kafka producer

flink local模式下启动 sink2kafka报错&#xff0c;具体报错如下 apache.kafka.common.KafkaException: Failed to construct kafka producerat org.apache.kafka.clients.producer.KafkaProducer.<init>(KafkaProducer.java:432)at org.apache.kafka.clients.producer.…

wps如何加载mathtype和Endnote

为了支持国产化软件&#xff0c;弃用office套装&#xff0c;现在改用wps办公软件&#xff0c;但是写作科技论文的时候还是会出现很多的不方便&#xff0c;比如文献引用、公式排版编号等等。尽管wps自带了公式编辑器&#xff0c;然鹅这可太不方便了&#xff0c;因此把几个技巧总…

谈谈你对 binder 的理解?

面试官提了一个问题&#xff0c;我们来看看 &#x1f60e;、&#x1f628; 和 &#x1f914;️ 三位同学的表现如何吧 &#x1f60e; 自认为无所不知&#xff0c;水平已达应用开发天花板&#xff0c;目前月薪 10k 面试官️&#xff1a;谈谈你对 binder 的理解 &#x1f60e;&a…

驱动开发-day10

驱动代码&#xff1a; #include <linux/cdev.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/gpio.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/mod_devicetable.h> #include …

本地服务器localhost:3000一直连接不上

1.检查使用端口3000的进程: 在Windows上,运行 netstat -ano | findstr :3000在Mac/Linux上,运行lsof -i :3000 这将列出当前使用端口3000的任何进程。您要终止这些进程以释放该端口。 2.检查防火墙规则: 确保您的防火墙允许连接到localhost:3000。在MacOS和Windows上,通常不…

【分布式能源的选址与定容】基于多目标粒子群算法分布式电源选址定容规划研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…