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

news2025/1/19 7:56:51

目录

一、认识拷贝构造函数

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.coloradmin.cn/o/755030.html

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

相关文章

内网使用JRebel及踩坑点

目录 前言外网正常使用JRebel下载安装插件授权 内网授权方式搭建本地授权服务器安装JRebel的电脑网络切换为手机热点 设置脱机离线模式设置自动重新部署时间间隔使用JRebel之前需要的配置项勾选compiler -> build project automatically设置代码热部署配置JRebel热部署的项目…

979.在二叉树中分配硬币

979.在二叉树中分配硬币 给定一个有 N 个结点的二叉树的根结点 root,树中的每个结点上都对应有 node.val 枚硬币,并且总共有 N 枚硬币。 在一次移动中,我们可以选择两个相邻的结点,然后将一枚硬币从其中一个结点移动到另一个结点…

Ubuntu新版静态IP设置

cd /etc/netplan直接编辑 sudo vi /etc/netplan/00-installer-config.yaml#network: # ethernets: # ens160: # dhcp4: true # version: 2network:version: 2ethernets:ens160:dhcp4: noaddresses: [172.17.10.23/24]optional: truegateway4: 172.17.10.1nameservers…

【Linux】无法连外网,只能用压缩包,环境搭建流程

【Linux】无法连外网,只能用压缩包,环境搭建流程 【一】JDK(1)下载jdk安装包(2)解压安装包(3)修改环境变量(4)测试是否安装成功 【二】安装mysql【1】5.7版本…

Prometheus、Grafana使用

文章目录 系统性能监控相关命令lscputopfreehtopdstatglancesiftopiptrafnethogs 监控软件Prometheus安装、使用将promethues做成服务监控其他机器 exportergrafana配置、使用 系统性能监控 相关命令 lscpu lscpu 是一个 Linux 命令,用于显示关于 CPU&#xff08…

使用git克隆非自己账号的项目

在地址的中https://或者http://后添加要使用的 账号名项目地址,就会提示输入对应账号的密码,进行克隆。 如果克隆别人的项目,你将别人给你的项目地址克隆,此时身份验证是别人的用户名,这时你输入自己的密码就会验证失败…

Centos 8 / TencentOS Server 3.1 安装 docker-ce

目录 前言安装 docker-ce设置Docker Hub 镜像缓存参考 前言 TencentOS Server 3.1(与 CentOS 8用户态完全兼容,配套基于社区5.4 LTS 内核深度优化的 tkernel4版本) 安装 docker-ce 先卸载老版本,没有老版本的跳过 yum remove docker \docker-client \d…

DBeaver连接华为高斯数据库 DBeaver连接Gaussdb数据库 DBeaver connect Gaussdb

DBeaver连接华为高斯数据库 DBeaver连接Gaussdb数据库 DBeaver connect Gaussdb 一、概述 华为GaussDB出来已经有一段时间,最近工作中刚到Gauss数据库。作为coder,那么如何通过可视化工具来操作Gauss呢? 本文将记录使用免费、开源的DBeaver来…

遥感目标检测(1)--R3Det

目录 一、概述 二、三个挑战 三、网络架构​编辑 1、旋转RetinaNet 2、精细化旋转RetinaNet 3、与RoIAlign(感兴趣区域插值)进行比较 4、消融实验与对比实验 一、概述 R3Det论文中提到一个端到端的精细化的单级旋转检测器,通过从粗到细…

JMeter自定义日志与日志分析

1 JMeter日志概览 JMeter与Java程序一样,会记录事件日志,日志文件保存在bin目录中,名称为jmeter.log。当然,我们也可以在面板中直接察看日志,点击右上角黄色标志物可以打开日志面板,再次点击收起。 可见&…

react 升级

1、查看react版本 当前开发项目的react版本从哪里看呢?其实就在package.json文件中,搜索"react",即可看到版本号 2、输入命令npm info react查看最新的react版本 3、执行命令 npm install --save react18.2.0 react-dom18.2.0 4…

Python中退出While循环的三种方法举例

Python中退出While循环的三种方法举例 在Python学习及编程应用中,常会使用while循环,对while循环条件设置不当可能导致进入死循环,本文将举例说明三种退出while循环的方法。 1.直接使用input函数 利用input函数使得输入值传递到while之后的…

使用jQuery的ajax提交图片信息

1 设置图片id(html) 首先,定义上传图片的id,根据上传文件的id获取图片信息: 注:图片的id应该设置在input标签里面 2 发送ajax请求(js) var formData new FormData(); formData.ap…

Hue编辑器命令执行

每一代人都有自己的命中注定的遗憾。遗憾,深深的遗憾。 唯一能自慰的是,我们曾真诚而充满激情地在这个世界上生活过,竭尽全力地劳动过, 并不计代价地将自己的血汗献给了不死的人类之树。 漏洞描述 Hue编辑器存在命令执行漏洞&am…

24种人格力量,好奇心的力量以及特征分析

人格力量是一种可支配的价值观,它能让人向往美好的远景,极大地促进人的工作发展。via认为好奇心是人格力量的种类之一。 借助via 24种人格力量测试,探索人格优势,可以帮助我们更好的发现自身的优势,发挥自己的潜能。从…

Sui x KuCoin Labs夏季黑客松第五批入围项目公布

经过40多天积极的报名以及精心的选拔,Sui x KuCoin Labs夏季黑客松现已完成对所有报名项目的筛选,最后一轮入围结果也在众人的期待中新鲜出炉。 了解入围项目的详细信息:https://hack.sui.io/demo-projects/入围项目的中文版介绍&#xff1a…

Java线程状态与状态转换

前言 在Java中,线程是多任务处理的基本单位,它可以并行执行多个任务。线程的状态描述了线程在其生命周期中的不同阶段。Java线程的状态可以分为以下几种: 线程状态 状态解释新建状态(New)线程被创建但尚未启动就绪状…

nginx配置IP白名单

1、添加IP白名单文件 在nginx目录的 conf 中添加文件 ip.conf,注意白名单文件不用添加任何注释,可以有空行 vi ip.conf 192.168.3.11 1;192.168.3.10 1; 192.168.0.112 1;2、配置nginx.conf 编辑http节点: http {# ...# geo IP whitelist…

一维二维水动力模拟、水质建模、复杂河网构建技术在环境影响评价、防洪评价与排污口论证等实际项目中的应用

目录 ​专题一、一维水动力模型模拟一河道水流的应用 专题二、一维复杂河网模型构建及建筑物设置 专题三、一维水质模型在环境影响评价中的应用 专题四、平面二维水动力模型的构建河验证 专题五、平面二维水动力模型在防洪影响评价中的应用 专题六、平面二维水动力水质模…

2023-07-13 Fitz完成PDF转TXT

Fitz完成PDF转TXT 前言 1、本机为Inspiron 5005,为64位,所用操作系统为Windos 10。所使用开发环境为Anaconda和jupyter notebook。 2、本教程主要实现了安装Anaconda和jupyter notebook,使用Fitz完成PDF转TXT。 Anaconda和Jupyter notebook简…