C++类与对象:拷贝构造函数&浅拷贝

2023-11-08

拷贝构造函数

目的:为了初始化对象,同构造函数

特点:

  1. 用户未定义,编译器会自动生成默认拷贝构造;
  2. 拷贝构造函数参数只有一个,参数类型必须是:本类类型对象的引用,(防止引用更改原对象的成员变量)用const修饰:const Date& d
  3. 由编译器自动调用;
  4. 编译器自动生成的拷贝构造函数,将原对象所有显式的内容进行拷贝,放在新的对象空间中(新对象与原对象共用同一块内存空间);

重点:使用拷贝构造函数的三种应用场景

  1. 用已存在的类类型对象创建新对象
  2. 函数参数为类类型
  3. 函数返回值为类类型

  • 初始化对象的方式:用已经存在的对象创建新对象,并初始化为相同值
  • 要重点区分拷贝构造对象时,使用=,是调用运算符重载函数还是拷贝构造
  • 就看被赋值的对象是否之前已经存在了,不存在就要按拷贝构造函数走

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

拷贝构造函数根据类中是否涉及到资源管理分成两种(与析构函数类似):

  1. 如果一个类中未涉及资源管理时,拷贝构造函数是否提供都可以
  2. 如果一个类中涉及资源管理时,拷贝构造函数必须显式提供,否则编译器默认生成的拷贝构造函数会出问题

默认拷贝构造

内置类型成员,进行值拷贝(又叫浅拷贝)
自定义类型成员,调用自定义类型类中的拷贝构造

  • 值拷贝:指将一个变量的值赋给另一个变量时,
    将源变量的值完全复制一份给目标变量,而不是两个变量共享同一个内存地址。
    这意味着如果源变量的值发生改变,目标变量的值不会受到影响
  • 例子:
    如果源变量pa是指针类型,其值存储的是动态开辟空间的首地址
    值拷贝就是将pa存储的值拷贝给pa’,所以两个指针指向同一块内存空间,析构的时候就会报错
  • 当类中包含数组成员时,拷贝构造函数会按照值拷贝的方式将整个数组拷贝过来,而不是仅拷贝数组首元素的地址
    因为数组是一个连续的内存块,数组名本身是指向数组首元素的指针,因此在进行值拷贝时,需要将整个内存块中的数据都复制到新对象的相应位置。

应用一:用已存在的类类型对象创建新对象

1.类中不涉及资源管理,可以使用默认拷贝构造函数

在这里插入图片描述

普通拷贝构造函数的函数体:

  • 拷贝构造的参数是类类型对象的引用,那么形参d就是实参d1的引用;
  • 将d对象的年月日的值赋给当前对象(当前对象就是调用拷贝构造的那个对象,就是d2)

参数使用const的原因:

  • 防止在函数体内部通过修改原对象的引用,修改了原对象

参数是引用的原因:会陷入无限递归

  • d1拷贝构造d2,先传参,再调用拷贝构造
    传参方式是传值,所以需要在传参过程中生成一份实参的拷贝设为d1’,
    要用d1去初始化d1’,d1’去初始化d2
  • d1’原先不存在是传参期间用d1拷贝构造出来的;所以就需要调又用拷贝构造,这次调用目的是为了创建d1’;
  • 创建d1’就又需要调用拷贝构造函数,从而陷入无限递归

可以调用默认拷贝构造函数的原因

  • d1对象中有三个成员变量(年月日),在往d2中拷贝时,逐字节拷贝这些数据,数据是值,不涉及资源管理

2.类中涉及资源管理

对于下列情况则用户必须提供不同于编译器自己生成的拷贝构造函数:

在这里插入图片描述

代码运行会在free处报错
s2在创建的时候,拷贝构造函数将s1所有属性通通复制给s2,包括s1变量空间地址与s2都相同,所以新旧对象在底层拥有同一份资源,两个对象销毁时,会将该份资源释放两次引起代码崩溃
调用拷贝构造函数的是为了拷贝原对象的数值,而不是对象中存储数据的地址;但是默认的拷贝构造函数却全部将原对象内容拷贝。

不能调用默认拷贝构造函数的原因:

  • 按字节拷贝,将d1中的数据原封不动的搬移,这种就是浅拷贝;
  • String对象中有一个成员变量,在往s2中拷贝时,逐字节拷贝这些数据,数据是char* 类型的指针(该指针指向动态开辟的空间);
  • 所以该对象只占4个字节,拷贝的时候会逐字节拷贝该指针变量保存的地址;对该空间重复释放就会出错

以上是用已存在的类类型对象创建新对象的应用场景

应用二:函数参数为类类型

void test (Date d);
{}

int main()
{
	Date d1(2015, 1, 1);  
	test(d1);
	return 0;
}

参数为类类型对象,就类似于C语言的传值:在传参过程中生成一份实参的拷贝;而要生成就需要调用拷贝构造函数

应用三:函数返回值为类类型

Date test (const Date& d);
{
	return d;
}

int main()
{
	Date d1(2015, 1, 1);
	Date d=test(d1);  
	return 0;
}
  1. 参数是引用类型,所以在传参过程中就不会生成实参的临时拷贝;
  2. 函数返回方式是以值的方式返回,在返回时需要创建临时对象;
    函数返回的时候不能直接返回对象d,因为d是函数中的一个局部对象(在函数参数处创建);
    函数结束后,该局部对象是要被销毁,因此必须要构造一个新的临时对象用来返回,而新的临时对象本来没有现在要通过d构造出来,那就是通过已经存在的d构造一个新的对象;
    所以需要调用拷贝构造函数

返回值的类型:

  1. 值方式返回 (int是内置类型,Date是自定义类型;两者统称值返回)
  2. 返回地址
  3. 返回引用
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++类与对象:拷贝构造函数&浅拷贝 的相关文章

随机推荐

  • python 3.7版本 打不开 python 3.8 保存的pickle文件

    1 问题描述 最近有一个pickle文件 当我使用python3 7 读取的时候报错 ValueError unsupported pickle protocol 5 查找原因发现是原始文件是用高版本的python解释器 比如3 8 保存的
  • 数据结构实验--表达式的后缀表示

    一 问题描述 表达式中包含运算对象 运算符和圆括号等 习惯上使用中缀表示 指运算符夹在两运算符对象中间 形式 计算表达式的值 涉及到运算符的优先级别 如先乘除后加减 括在一对圆括号中的子表达式必须先计算 因此 圆括号可视为特殊的运算符 具有
  • Linux--遇见的一些小错误

    一 问题 linux下运行出现 sh 1 pause not found 原因 在windows系统下 使用此语句 但linux不认识 system pause 解决方法 删去system pause 二 问题 编程时出现 Warning
  • Unity3D——AR小游戏

    文章目录 有趣的AR小游戏制作 环境准备 具体实现 替换Camera 上传识别卡 下载识别卡模型 编辑游戏对象 实验环境是否配置成功 导入Lean Touch脚本 制作成Android应用 游戏制作 实验结果 有趣的AR小游戏制作 环境准备
  • MySQL 5.1中文参考手册 - 学习笔记

    MySQL 5 1中文参考手册地址 http dev mysql com doc refman 5 1 zh index html 学习笔记及重要点 由于最近使用MySQL数据库的机会越来越多 所以看来这次要认真的学习一下了 以往只懂得皮毛
  • 线程-Linux下的轻量级进程

    首先我们知道 每个进程都是在各自独立的地址空间上运行 如果要同时完成好几个任务 比如你一边在下载软件 另一边在进行着其他的操作 那么试想一下 可不可以在一个进程里面把这几个事件同时进行呢 这里就要提到线程的概念了 但其实Linux中 并没有
  • Unity 实用代码 小工具

    Unity 实用代码 小工具 Unity 屏幕截图 全屏截图方法 全屏截图方法 带委托事件 自定义截图方法 自定义截图方法 带委托 延迟工具 携程延迟方法 携程延迟带委托方法 场景加载 场景加载 方法 场景加载方法 带委托 异步场景加载 方
  • Warning: This development build of composer is over 60 days old

    今天查看了一下服务器安装的Composer版本 报了一个警告 意思是安装已经超过60天了 需要执行 usr bin composer self update 升级到最新版本 然后我就执行了 再次查看版本确实更新到官方最新的1 7 3版本 但
  • PTA7(python3)

    python程序设计07 字符串与正则表达式 7 1 找最后的字符 30 分 7 2 重要的事情说N遍 20 分 7 3 号码牌的制作 10 分 7 4 统计字符串中指定字符的个数 30 分 7 5 字符串消除空格 30 分 7 6 统计指
  • 三分钟弄懂物联网流行协议——MQTT

    MQTT Message Queue Telemetry Transport 翻译成中文就是 遥测传输协议 其主要提供了订阅 发布两种消息模式 更为简约 轻量 易于使用 特别适合于受限环境 带宽低 网络延迟高 网络通信不稳定 的消息分发 属
  • C++中vector迭代器失效问题及其解决方法

    C 中vector迭代器失效问题及其解决方法 迭代器的主要作用就是让算法能够不用关心底层数据结构 其底层实际就是一个指针 或者是对指针进行了封装 比如 vector的迭代器就是原生态指针T 因此迭代器失效 实际就是迭代器底层对应指针所指向的
  • php socket error 111,php中的socket_connect上的“连接被拒绝”错误

    我试图将一些代码从perl转换为php Perl代码如下所示 my handle Connect port host 我试图使用socket在PHP中做同样的事情 我试过socket create和socket connect socket
  • Windows(10/11)端vscode开发、调试远程Linux(Ubuntu14.04)端c++ 开发环境部署步骤

    1 安装vscode 进入https code visualstudio com 即vsocde官网选择Windows x64版本下载并安装 安装过程中推荐勾选往右键菜单添加通过vscode打开文件夹的选项 2 vsocde插件安装 打开v
  • 《影响力》第七章:稀缺

    稀缺 物以稀为贵 稍纵即逝 越是得不到就越觉得香 这是为啥 至于是不是真的香 由于得不到 也不得而知 例如 我有一个朋友觉得佐佐木希如果能娶来做老婆是很香的 简直就是夫复何求 然而这世上当真有人会把女神娶回家然后出轨还家暴 这说来还真是让人
  • 判断点是否在任意多边形内(java)

    import java util ArrayList public class Test public static void main String args double px 113 0253 double py 23 98049 A
  • 文心一言和讯飞星火全面对比测试:(三)常识问题

    前文回顾 在 一 语言理解能力测试中 我们主要测试了两个大语言模型对复杂语义的理解 对文章情绪的识别 对文章进行摘要总结 对文章进行要素提取 测试结果表明 在语言理解能力上 除了有些问题他拒绝回答之外 讯飞星火的表现明显要好于文心一言 可以
  • 函数和windows对象 有惊喜✔

    一 函数 函数的定义 类似于Java中的方法 是完成特定任务的代码语句块 1 系统函数 eval lt 表达式 gt 得到一个文本框的值 表单 例 var sname eval doucment form sname value parse
  • Java多线程导致CPU占用100%解决及线程池正确关闭方式

    文章目录 前言 一 cpu占用高排查问题 二 解决办法 使用AtomicLong 统计线程是否完成 再执行executor submit 提交新的任务导队列中 三 多线程关闭与令牌限流 前言 情景 1000万表数据导入内存数据库 按分页大小
  • 超详细java web实验环境 (4) Eclipse配置Tomcat配置

    目录 一 确保Tomcat服务器处于关闭状态 二 在Eclipse中配置Tomcat 三 测试Tomcat是否配置完成 四 若出现异常 一 确保Tomcat服务器处于关闭状态 二 在Eclipse中配置Tomcat 1 打开Eclipse
  • C++类与对象:拷贝构造函数&浅拷贝

    标题 拷贝构造函数 默认拷贝构造 应用一 用已存在的类类型对象创建新对象 1 类中不涉及资源管理 可以使用默认拷贝构造函数 2 类中涉及资源管理 应用二 函数参数为类类型 应用三 函数返回值为类类型 拷贝构造函数 目的 为了初始化新对象 同