掘根宝典之C++深复制与浅复制(复制构造函数,默认复制构造函数)

到目前为止我们已经学了构造函数,默认构造函数,析构函数:http://t.csdnimg.cn/EOQxx

转换函数,转换构造函数:http://t.csdnimg.cn/kiHo6

友元函数:http://t.csdnimg.cn/To8Tj

接下来我们来学习一个新函数——复制构造函数

复制构造函数

复制构造函数用于将一个对象复制到新创建的对象中。也就是说它用于初始化过程中(包括按值传递参数),而不是常规的赋值过程中

类的复制构造函数原型通常如下:

class name(const class name&);

什么时候调用复制构造函数

新建一个对象并将其初始化为同类现有对象时,复制构造函数将被调用。

这在很多情况下都可能会发生,最常见的情况是将新对象显式的初始化为现有对象。

比如下面这些情况

#include<iostream>
using namespace std;
class AA
{
private:int a_;
public:AA(const AA& t){a_ = t.a_;}AA(int a){a_ = a;}
};
int main()
{AA t ={2} ;AA w = t;//下面4句都将调用复制构造函数AA e = AA(t);AA* r = new AA(t);AA y(t);
}

还有一些情况是每当程序生成了程序副本时,编译器都将使用复制构造函数。

准确的说是当函数按值传递对象和函数返回对象时,都将使用复制构造函数。

我们举个例子

#include<iostream>
using namespace std;
class AA
{
private:int a_;
public:AA(const AA& t){cout << "调用了复制构造函数" << endl;a_ = t.a_;}AA(int a){a_ = a;}void A(AA a){cout << a.a_ << endl;}AA B(AA&t){AA w(t);return w;}
};int main()
{AA e = { 3 };AA r = { 9 };e.A(r);AA t = e.B(r);}

结果是

调用了复制构造函数
9
调用了复制构造函数

默认的复制构造函数

如果我们没有提供复制构造函数,编译器就会自动提供一个复制构造函数,这个复制构造函数也叫默认复制构造函数。默认的复制构造函数逐个复制非静态成员(成员复制也叫浅复制)

我们可以看个例子

#include<iostream>
using namespace std;
class AA
{
private:int a_;
public:AA(int a){a_ = a;}
};
int main()
{AA a = { 9 };AA t = a;/*与下面的语句等效AA t;t.a_=a.a_;*/
}

浅复制

默认复制构造函数的浅复制

我们先来看这么一个例子

#include<iostream>
using namespace std;
class AA
{
private:int*a_;
public:AA(int a){a_ = new int(a);}void A(){cout << *a_ << endl;}
};
int main()
{AA a(9);a.A();//结果是9AA t = a;t.A();//结果是9}

结果是 

9
9

可能现在你还没发现什么异样,那我们再看下面这个例子

#include<iostream>
using namespace std;
class AA
{
private:int*a_;
public:AA(int a){a_ = new int(a);}void A(){cout << *a_ << endl;}void shan(){delete a_;}
};
int main()
{AA a(9);a.A();AA t = a;t.A();a.shan();t.A();}

结果是

9
9
-572662307

我们会发现,啊嘞?第三行怎么是一堆乱码。这是什么情况呢?

原来啊,上面这个情况在对对象进行复制时,只简单地复制对象的成员变量值,而没有复制对象内部的动态分配的资源,这个叫浅复制

这是因为浅复制只复制了指针的值,而没有复制指针所指向的内存。因此,两个对象的a_成员变量指向同一块内存,修改任何一个对象的a_值都会影响到另一个对象。

自定义复制构造函数的浅复制

不用以为我们定义了复制构造函数,进行的复制就不叫浅复制了。实际上,下面这种也叫浅复制。

上例子

#include<iostream>
using namespace std;
class AA
{
private:int*a_;
public:AA(int a){a_ = new int(a);}AA(const AA&a){a_ = a.a_;}void A(){cout << *a_ << endl;}void shan(){delete a_;}
};
int main()
{AA a (9);a.A();AA t = a;t.A();a.shan();t.A();}

结果是

9
9
-572662307

 惊奇的发现和上面的情况是一样的,这是因为我们定义的复制构造函数也仅仅是复制指针的值罢了,没有开辟新的内存块

浅复制的用武之地

浅复制在某些情况下可能是合适的,例如对于只包含基本类型成员变量的简单对象。但是对于包含动态分配的资源或指针成员变量的对象来说,浅复制可能会导致错误或内存泄漏。在这种情况下,应该使用深复制来保证每个对象都有独立的资源副本。

深复制

解决上面这种类设计中的问题的方法是进行深度复制。也就是说复制构造函数应当复制值并将副本的地址赋给a_成员,而不仅仅是复制值地址。

我们直说可能有点难懂,看个图就知道了

 

 必须定义复制构造函数的原因是一些类成员是使用new初始化的,指向数据的指针,而不是数据本身。

什么时候使用深复制?什么时候用浅复制?

如果类里包含了使用new初始化的指针成员,应当定义一个复制构造函数,以复制指向的数据,而不是指针,这被称作深度复制。浅复制仅浅浅的复制指针信息,而不会深入复制new出来的那块内存。

实现深复制

实现深复制就必须自己定义一个会另外开辟内存的复制构造函数,而不是简单的逐成员复制

我们看个例子

#include<iostream>
using namespace std;
class AA
{
private:int*a_;
public:AA(int a){a_ = new int(a);}AA(const AA&a){int* w = new int(*a.a_);//深度复制的体现a_ = w;}void A(){cout << *a_ << endl;}void shan(){delete a_;}
};
int main()
{AA a = { 9 };a.A();AA t = a;t.A();a.shan();t.A();}

这便是深度复制了

赋值运算符里的浅复制

我们得先知道,C++允许把一个对象赋值给另一个同类对象

初始化的时候总是会调用复制构造函数,而使用=运算符时也允许调用赋值运算符

将已有对象赋给另一个对象时,将采用重载的赋值运算符

初始化时不一定会使用赋值运算符

使用赋值运算符的情况和那个隐式复制构造函数一样,都是浅复制的问题。

我们可以使用友元函数来重载运算符=,使其成为深度赋值

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

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

相关文章

python毕设选题 - 大数据全国疫情数据分析与3D可视化 - python 大数据

文章目录 0 前言1 课题背景2 实现效果3 设计原理4 部分代码5 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长自己做的…

2024阿里云云服务器ECS价格表出炉

2024年最新阿里云服务器租用费用优惠价格表&#xff0c;轻量2核2G3M带宽轻量服务器一年61元&#xff0c;折合5元1个月&#xff0c;新老用户同享99元一年服务器&#xff0c;2核4G5M服务器ECS优惠价199元一年&#xff0c;2核4G4M轻量服务器165元一年&#xff0c;2核4G服务器30元3…

【Funny Game】 人生重开模拟器

目录 【Funny Game】 人生重开模拟器&#xff01; 人生重开模拟器&#xff01; 文章所属专区 Funny Game 人生重开模拟器&#xff01; 人生重开模拟器&#xff0c;让你体验从零开始的奇妙人生。在这个充满惊喜和挑战的游戏中&#xff0c;你可以自由选择性别、出生地、家庭背景…

String.format()详细用法

String 类有一个强大的字符串格式化方法 format()。下面是常用的方法总结。 一、占位符类型 String formatted String.format("%s今年%d岁。", "小李", 25); // "小李今年25岁。" 二、字符串和整数格式化 // 将第二个入参拼接到模板中,入参长…

职业性格在求职应聘和跳槽中的作用

性格测试对跳槽者的影响大不大&#xff1f;首先我们要弄清楚两个问题&#xff0c;性格对我们的职业生涯又没有影响&#xff0c;性格测试是什么&#xff0c;职场中有哪些应用&#xff1f;性格可以说从生下来就有了&#xff0c;随着我们的成长&#xff0c;我们的性格也越来越根深…

大模型训练流程(一)预训练

预训练GPU内存分析&#xff1a; GPU占用内存 模型权重 梯度 优化器内存&#xff08;动量估计和梯度方差&#xff09; 中间激活值*batchsize GPU初始化内存 训练流程 &#xff08;选基座 —> 扩词表 —> 采样&切分数据 —> 设置学习参数 —> 训练 —>…

什么是美颜SDK?美颜SDK在短视频平台中的作用探究

在这个以视频为主导的平台上&#xff0c;美颜技术在其中扮演了不可或缺的角色。本文将探讨美颜SDK的本质&#xff0c;以及它在短视频平台中所发挥的作用。 一、什么是美颜SDK&#xff1f; 美颜SDK是一种软件开发工具包&#xff0c;其主要功能是通过算法对图像进行美化处理。…

【教3妹学编程-算法题】人员站位的方案数 II

2哥 : 3妹&#xff0c;今天第一天上班啊&#xff0c;开工大吉~ 3妹&#xff1a;2哥&#xff0c;开工大吉鸭&#xff0c;有没有开工红包&#xff1f; 2哥 : 我们公司比较扣&#xff0c;估计不会发的。 3妹&#xff1a;我们公司估计也一样&#xff0c;不过依然挡不住我打工人的热…

HarmonyOS router页面跳转

默认启动页面index.ets import router from ohos.router import {BusinessError} from ohos.baseEntry Component struct Index {State message: string Hello World;build() {Row() {Column() {Text(this.message).fontSize(50).fontWeight(FontWeight.Bold)//添加按钮&am…

【 JS 进阶 】原型对象、面向对象

目标 了解构造函数原型对象的语法特征&#xff0c;掌握 JavaScript 中面向对象编程的实现方式&#xff0c;基于面向对象编程思想实现 DOM 操作的封装。 了解面向对象编程的一般特征掌握基于构造函数原型对象的逻辑封装掌握基于原型对象实现的继承理解何为原型链及其作用能够处理…

C++Qt:noteBookPro_01

一、创建项目 选择Qt Widgets 常用的是QWidgets和MainWindow。两者的区别&#xff1a; QWidgets用于简单的窗口&#xff0c;没有内置的菜单栏、工具栏和状态栏。适用于简单专用的应用程序&#xff0c;不需要复杂的界面组件。 MainWindow是包含完整的菜单栏、工具栏和状态栏的主…

Linux 主机数据拷贝与 Linux 服务器之间拷贝文件的方法

Linux 主机数据拷贝与 Linux 服务器之间拷贝文件的方法 1. 使用 scp 命令2. 使用 rsync 命令3. 使用 scp 和 rsync 的图形界面工具4. 使用 FTP/SFTP 协议总结与比较 在 Linux 系统中&#xff0c;数据拷贝是日常操作中的常见需求&#xff0c;尤其是在不同主机或服务器之间进行文…

近场2D beamforming Heatmap图

文章目录 想法代码目前啥样想法 参考论文Beam Focusing for Near-Field Multiuser MIMO Communications,可视化beam focusing效应 代码 clc; clear;% 网格范围 D = 1; % 整个均匀平面阵列的孔径 lambda = 1e-2; % 波长0.01m,单位:米 30GhzN_d = floor(2 * D / lambda); %…

PS自由变换的小技巧--墙面广告牌

墙面广告牌&#xff0c;如何用PS做出看上去特别真实的一个效果 1.首先&#xff0c;我们会有墙面跟广告栏2个图层 2.然后将广告牌复制一层 3.接着用钢笔工具画出墙面的透视&#xff0c;也就是两条线&#xff0c;这两条线的交叉点就是墙面的透视点 4.接着选中广告牌复制图层&…

杨氏矩阵和杨辉三角

杨氏矩阵 有一个数字矩阵&#xff0c;矩阵的每行从左到右是递增的&#xff0c;矩阵从上到下是递增的&#xff0c;请编写程序在这样的矩阵中查找某个数字是否存在。 要求&#xff1a;时间复杂度小于O(N); 分析 若要满足要求时间复杂度小于O(N)&#xff0c;就不能每一行一个个…

TCP流量控制+拥塞控制

流量控制&#xff1a; 目标&#xff1a;流量控制主要解决的是发送方和接收方之间处理能力的不匹配问题。它的目的是确保发送方不会发送数据过快&#xff0c;以至于接收方无法及时接收并处理这些数据&#xff0c;从而避免数据包在网络中堆积和丢失。实现方式&#xff1a;在TCP协…

【.NET Core】深入理解async 和 await 理解

【.NET Core】深入理解async 和 await 理解 文章目录 【.NET Core】深入理解async 和 await 理解一、概述二、async异步执行机制理解三、async与await应用3.1 async与await简单应用3.2 带有返回值async与await应用 四、async和await中常见问题总结4.1 当方法用async标识时&…

7条策略,提升可视化大屏的科技感,值得收藏。

以下是从设计角度上提升科技感的几条建议&#xff1a; 使用现代化的字体&#xff1a; 选择现代化的字体能够让大屏幕看起来更加科技感。比如&#xff0c;Sans-serif字体、Roboto字体、Lato字体等都是现代化的字体。 设计简洁、清晰的图表&#xff1a; 图表是可视化大屏设计中…

端口号被占用怎么解决

1、快捷键"winR"打开运行&#xff0c;在其中输入"cmd"命令&#xff0c;回车键打开命令提示符。 2、进入窗口后&#xff0c;输入"netstat -ano"命令&#xff0c;可以用来查看所有窗口被占用的情况。 比如端口号为7680的端口被占用了&#xff0c…

一次失败的面试经历:我只想找个工作,你却用面试题羞辱我

金三银四就要来了&#xff0c;即将又是一波求职月&#xff0c;面对跳槽的高峰期&#xff0c;很多软件测试人员都希望能拿一个满意的高薪offer&#xff0c;但是随着招聘职位的不断增多&#xff0c;面试的难度也随之加大&#xff0c;而面试官更是会择优录取 小王最近为面试已经焦…