C++拷贝构造函数详解

2023-11-07

一. 什么是拷贝构造函数

首先对于普通类型的对象来说,它们之间的复制是很简单的,例如:


而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。
下面看一个类对象拷贝的简单例子。

运行程序,屏幕输出100。从以上代码的运行结果可以看出,系统为对象 B 分配了内存并完成了与对象 A 的复制过程。就类对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的

下面举例说明拷贝构造函数的工作过程。


CExample(const CExample& C) 就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它必须的一个参数是本类型的一个引用变量


二. 拷贝构造函数的调用时机

在C++中,下面三种对象需要调用拷贝构造函数!
1. 对象以值传递的方式传入函数参数


调用g_Fun()时,会产生以下几个重要步骤:
(1).test对象传入形参时,会先会产生一个临时变量,就叫 C 吧。
(2).然后调用拷贝构造函数把test的值给C。 整个这两个步骤有点像:CExample C(test);
(3).等g_Fun()执行完后, 析构掉 C 对象。

2. 对象以值传递的方式从函数返回


当g_Fun()函数执行到return时,会产生以下几个重要步骤:
(1). 先会产生一个临时变量,就叫XXXX吧。
(2). 然后调用拷贝构造函数把temp的值给XXXX。整个这两个步骤有点像:CExample XXXX(temp);
(3). 在函数执行到最后先析构temp局部变量。
(4). 等g_Fun()执行完后再析构掉XXXX对象。

3. 对象需要通过另外一个对象进行初始化;

后两句都会调用拷贝构造函数。


三. 浅拷贝和深拷贝

1. 默认拷贝构造函数

    很多时候在我们都不知道拷贝构造函数的情况下,传递对象给函数参数或者函数返回对象都能很好的进行,这是因为编译器会给我们自动产生一个拷贝构造函数,这就是“默认拷贝构造函数”,这个构造函数很简单,仅仅使用“老对象”的数据成员的值对“新对象”的数据成员一一进行赋值,它一般具有以下形式:

 
    当然,以上代码不用我们编写,编译器会为我们自动生成。但是如果认为这样就可以解决对象
的复制问题,那就错了,让我们来考虑以下一段代码:

  这段代码对前面的类,加入了一个静态成员,目的是进行计数。在主函数中,首先创建对象rect1,输出此时的对象个数,然后使用rect1复制出对象rect2,再输出此时的对象个数,按照理解,此时应该有两个对象存在,但实际程序运行时,输出的都是1,反应出只有1个对象。此外,在销毁对象时,由于会调用销毁两个对象,类的析构函数会调用两次,此时的计数器将变为负数。

说白了,就是拷贝构造函数没有处理静态数据成员。

出现这些问题最根本就在于在复制对象时,计数器没有递增,我们重新编写拷贝构造函数,如下

2. 浅拷贝

    所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。大多情况下“浅拷贝”已经能很好地工作了,但是一旦对象存在了动态成员,那么浅拷贝就会出问题了,让我们考虑如下一段代码:

    在这段代码运行结束之前,会出现一个运行错误。原因就在于在进行对象复制时,对于动态分配的内容没有进行正确的操作。我们来分析一下:

    在运行定义rect1对象后,由于在构造函数中有一个动态分配的语句,因此执行后的内存情况大致如下:

 

 

    在使用rect1复制rect2时,由于执行的是浅拷贝,只是将成员的值进行赋值,这时 rect1.p = rect2.p,也即这两个指针指向了堆里的同一个空间,如下图所示:

 

当然,这不是我们所期望的结果,在销毁对象时,两个对象的析构函数将对同一个内存空间释放两,这就是错误出现的原因。我们需要的不是两个p有相同的值,而是两个p指向的空间有相同的值,解决办法就是使用“深拷贝”。


3. 深拷贝

    在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,而应该重新动态分配空间,如上面的例子就应该按照如下的方式进行处理:

此时,在完成对象的复制后,内存的一个大致情况如下:

 

此时rect1的p和rect2的p各自指向一段内存空间,但它们指向的空间具有相同的内容,这就是所谓的“深拷贝”。


3. 防止默认拷贝发生

    通过对对象复制的分析,我们发现对象的复制大多在进行“值传递”时发生,这里有一个小技巧可以防止按值传递——声明一个私有拷贝构造函数。甚至不必去定义这个拷贝构造函数,这样因为拷贝构造函数是私有的,如果用户试图按值传递或函数返回该类对象,将得到一个编译错误,从而可以避免按值传递或返回对象。

四. 拷贝构造函数的几个细节

1. 拷贝构造函数里能调用private成员变量吗?
解答:
这个问题是在网上见的,当时一下子有点晕。其时从名子我们就知道拷贝构造函数其时就是
一个特殊的构造函数,操作的还是自己类的成员变量,所以不受private的限制。


2. 以下函数哪个是拷贝构造函数,为什么?


解答:对于一个类X, 如果一个构造函数的第一个参数是下列之一:
a) X&
b) const X&
c) volatile X&
d) const volatile X&
且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数.


3. 一个类中可以存在多于一个的拷贝构造函数吗?
解答:
类中可以存在超过一个拷贝构造函数。


注意,如果一个类中只存在一个参数为 X& 的拷贝构造函数,那么就不能使用const X或volatile X的
对象实行拷贝初始化.


如果一个类中没有定义拷贝构造函数,那么编译器会自动产生一个默认的拷贝构造函数。
这个默认的参数可能为 X::X(const X&)X::X(X&),由编译器根据上下文决定选择哪一个。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++拷贝构造函数详解 的相关文章

  • 删除文件的最后 10 个字符

    我想删除文件的最后 10 个字符 说一个字符串 hello i am a c learner 是文件内的数据 我只是希望该文件是 hello i am a 文件的最后 10 个字符 即字符串 c learner 应在文件内消除 解决方案 将
  • 调用 McAfee 病毒扫描引擎

    我收到客户的请求 要求使用他们服务器上的 McAfee 病毒扫描将病毒扫描集成到应用程序中 我做了一些调查 发现 McScan32 dll 是主要的扫描引擎 它导出各种看起来有用的函数 我还发现提到了 McAfee Scan Engine
  • 通过引用传递 [C++]、[Qt]

    我写了这样的东西 class Storage public Storage QString key const int value const void add item QString int private QMap
  • 如何在 Cassandra 中存储无符号整数?

    我通过 Datastax 驱动程序在 Cassandra 中存储一些数据 并且需要存储无符号 16 位和 32 位整数 对于无符号 16 位整数 我可以轻松地将它们存储为有符号 32 位整数 并根据需要进行转换 然而 对于无符号 64 位整
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • 如何从本机 C(++) DLL 调用 .NET (C#) 代码?

    我有一个 C app exe 和一个 C my dll my dll NET 项目链接到本机 C DLL mynat dll 外部 C DLL 接口 并且从 C 调用 C DLL 可以正常工作 通过使用 DllImport mynat dl
  • 如何在 C++ 中标记字符串?

    Java有一个方便的分割方法 String str The quick brown fox String results str split 在 C 中是否有一种简单的方法可以做到这一点 The 增强分词器 http www boost o
  • 访问外部窗口句柄

    我当前正在处理的程序有问题 这是由于 vista Windows 7 中增强的安全性引起的 特别是 UIPI 它阻止完整性级别较低的窗口与较高完整性级别的窗口 对话 就我而言 我想告诉具有高完整性级别的窗口进入我们的应用程序 它在 XP 或
  • C# 列表通用扩展方法与非通用扩展方法

    这是一个简单的问题 我希望 集合类中有通用和非通用方法 例如List
  • 两个静态变量同名(两个不同的文件),并在任何其他文件中 extern 其中一个

    在一个文件中将变量声明为 static 并在另一个文件中进行 extern 声明 我认为这会在链接时出现错误 因为 extern 变量不会在任何对象中看到 因为在其他文件中声明的变量带有限定符 static 但不知何故 链接器 瑞萨 没有显
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • 结构体的内存大小不同?

    为什么第一种情况不是12 测试环境 最新版本的 gcc 和 clang 64 位 Linux struct desc int parts int nr sizeof desc Output 16 struct desc int parts
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • 为什么 C# 2.0 之后没有 ISO 或 ECMA 标准化?

    我已经开始学习 C 并正在寻找标准规范 但发现大于 2 0 的 C 版本并未由 ISO 或 ECMA 标准化 或者是我从 Wikipedia 收集到的 这有什么原因吗 因为编写 审查 验证 发布 处理反馈 修订 重新发布等复杂的规范文档需要
  • 如何在 Linq to SQL 中使用distinct 和 group by

    我正在尝试将以下 sql 转换为 Linq 2 SQL select groupId count distinct userId from processroundissueinstance group by groupId 这是我的代码
  • C 函数 time() 如何处理秒的小数部分?

    The time 函数将返回自 1970 年以来的秒数 我想知道它如何对返回的秒数进行舍入 例如 对于100 4s 它会返回100还是101 有明确的定义吗 ISO C标准没有说太多 它只说time 回报 该实现对当前日历时间的最佳近似 结
  • 如何在 Android 中使用 C# 生成的 RSA 公钥?

    我想在无法假定 HTTPS 可用的情况下确保 Android 应用程序和 C ASP NET 服务器之间的消息隐私 我想使用 RSA 来加密 Android 设备首次联系服务器时传输的对称密钥 RSA密钥对已在服务器上生成 私钥保存在服务器
  • 有没有办法让 doxygen 自动处理未记录的 C 代码?

    通常它会忽略未记录的 C 文件 但我想测试 Callgraph 功能 例如 您知道在不更改 C 文件的情况下解决此问题的方法吗 设置变量EXTRACT ALL YES在你的 Doxyfile 中
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • C# 中的 IPC 机制 - 用法和最佳实践

    不久前我在 Win32 代码中使用了 IPC 临界区 事件和信号量 NET环境下场景如何 是否有任何教程解释所有可用选项以及何时使用以及为什么 微软最近在IPC方面的东西是Windows 通信基础 http en wikipedia org

随机推荐

  • Web前端:html烟花代码

    代码如下
  • 【c语言】新手初学while循环,for循环的一些想法与思考

    循环语句有三种 while循环 for循环 do while循环 由于只学了前两种 因此只能将两种进行比较 但在比较两者前 我认为新手有必要认真的学习良好的代码习惯 这里我使用的是 高质量 C C 编程指南 尽管年代比较久远但依旧受益良多
  • 一、基础架构

    架构是什么 软件架构指软件系统的顶层结构 主要是由系统是一群关联个体组成 个体可以是模块或者子系统 他们按照某种特定的规则来运作和协作 系统架构的目的 架构设计的主要目的是为了解决软件系统复杂度带来的问题 要有的放矢 不是胡编乱造 这个结论
  • HTML5特效动画

    11 10款造型奇特的CSS3进度条 Loading动画DEMO演示 10款造型奇特的CSS3进度条 Loading动画 今天我们要分享10款造型奇特的CSS3进度条 Loading动画 这10款进度条应用不仅有创意的外观 而且还有非常特别
  • VS Code插件live server的安装和使用

    https blog csdn net sinat 37024730 article details 128902967
  • MS08-067远程代码执行漏洞(CVE-2008-4250)

    MS08 067远程代码执行漏洞 CVE 2008 4250 Windows Server服务RPC请求缓冲区溢出漏洞复现 文章目录 MS08 067远程代码执行漏洞 CVE 2008 4250 Windows Server服务RPC请求缓
  • anaconda使用系列教程--5)安装anaconda环境到指定位置

    背景 anaconda的环境都比较大 如果安装到根目录很快就会把根目录占满 最好能指定新建环境的安装路径 方法 方法就是在conda create命令加上选项 prefix即可 安装虚拟环境到指定路径的命令如下 conda create p
  • 数据库文件加密

    配置MySQL数据库连接 spring datasource driver class name com mysql jdbc Driver spring datasource url jdbc mysql 121 196 xxx xxx
  • STM32自己写printf函数方法(不用重定向)

    stm32有几个串口可以使用 当我们想用printf打印数据到上位机时 通常是使用fput重定向 当我们要多个串口使用printf格式函数时 可以在写一个类似printf的函数 include
  • PyTorch 源码解读之 nn.Module:核心网络模块接口详解

    目录 0 设计 1 nn Module 实现 1 1 常用接口 1 1 1 init 函数 1 1 2 状态的转换 1 1 3 参数的转换或转移 1 1 4 Apply 函数 1 2 属性的增删改查 1 2 1 属性设置 1 2 2 属性删
  • Spring Boot(一):开始使用

    背景 其实Spring Boot很早就使用过 但由于之前未做深入的研究且目前工作中所使用的技术也并未涉及 所以导致对其使用依然不是很熟悉 正是因为用过Spring Boot才懂得它的好用之处 但目前开发的项目使用的技术较为落后 导致开发效率
  • 探究:秒杀系统

    1 秒杀系统的特点 瞬时高并发 2 预防措施 2 1 流量限制 对于一个相同的用户 限制请求的频次 对于一个相同的IP 限制请求的频次 验证码 减缓用户请求的次数 活动开启之前 按钮先置灰 防止无效的请求流入系统 给系统造成冲击 2 2 页
  • LOL-v2数据集和VE-LOL数据集的区别

    LOL v2数据集和VE LOL数据集的区别 LOL v2 LOL v2数据集 64 包括两个不同的子集 即LOL v2 real和LOL v2 synthetic LOL v2 real子集是通过改变ISO和曝光时间在真实场景中捕获的 包
  • git merge&rebase区别

    merge rebase两个分支合并操作 各有利弊 我们先看看表现吧 假如master和feature分支如下 如果我们merge操作 我们看到 合并时候 作为一个新提交作为一个新节点 head指针移动到最新master分支 feature
  • linux定时执行php脚本方法

    网站运营过程中 经常会遇到需要定时执行php脚本的情况 这次介绍linux系统定时执行php脚本方法 crontab crontab的服务进程名为crond 英文意为周期任务 crontab在Linux主要用于周期定时任务管理 通常安装操作
  • 胡立阳:如何判断股价见底

    胡立阳 如何判断股价见底 发表时间 2007年11月28日 17时56分 评论 浏览 248 213844 腾讯证券 最近A股指数在6100点一路下滑 跌破半年线 国内各个媒体众说纷纭 观点不一 都在判断底部到底在哪里 就此问题 腾讯证券连
  • AntD的Table表头title加Icon图标和气泡提示Tooltip

    如图 想在Table表单中的描述后面加上Icon图标和气泡提示Tooltip 第一时间找了AntD文档 找到搜索功能及相应图标 filterDropdown和filterIcon 也在百度上找到https www jianshu com p
  • QT基础(入门)

    对QT文件中的 pro文件解释 QT core gui QT包含的模块 QT中的快捷键 注释 ctr 运行 ctrl r 编译 ctrl b 查找 ctrl f 整行移动 ctrl shift 向上键 向下键 帮助文档 F1 自动对齐 ct
  • Android Studio NDK JNI 编程最小白最简单入门Demo

    Android Studio 编写JNI有两种方式 通过ndk build编写 和eclipse类似 需要配置Android mk Application mk文件 之前的一些开源库还是使用此种方式编写 so 因此还是需要了解此种方式 通过
  • C++拷贝构造函数详解

    一 什么是拷贝构造函数 首先对于普通类型的对象来说 它们之间的复制是很简单的 例如 int a 100 int b a 而类对象与普通对象不同 类对象内部结构一般较为复杂 存在各种成员变量 下面看一个类对象拷贝的简单例子 include