c++的默认拷贝构造函数,从深度拷贝和浅拷贝说起

2023-11-10

本文收录于微信公众号「 LinuxOK 」,ID为:Linux_ok,关注公众号第一时间获取更多技术学习文章。

####1. c++类的默认拷贝构造函数的弊端
c++类的中有两个特殊的构造函数,(1)无参构造函数,(2)拷贝构造函数。它们的特殊之处在于:
(1)当类中没有定义任何构造函数时,编译器会默认提供一个无参构造函数且其函数体为空;
(2)当类中没有定义拷贝构造函数时,编译器会默认提供一个拷贝构造函数,进行成员变量之间的拷贝。(这个拷贝操作是浅拷贝)

这里只讲拷贝构造函数。在c语言中,

int a = 5;	//初始化
int b;
b = 6;		//赋值

上面的初始化及赋值操作是最正常不过的语法,c++语言肩挑兼容c语言语法的责任,所以在类的设计上,也兼容这种操作:

class cls
{
pubic:
	//...
}

int main(void)
{
	cls c1;
	cls c2 = c1;	//初始化类,还可以 cls c2(c1);
	cls c3;
	
	c3 = c1;		//赋值类
	
	//...
	
	return 0;
}

如上的初始化类需要调用到cls类的默认实现的拷贝构造函数,为类赋值需要调用的是cls类的默认实现的赋值操作符重载函数,它们都是浅度拷贝的。前者其原型为:

cls(const cls& c)

默认的拷贝构造函数存在弊端,看如下类定义:

class TestCls{
public:
	int a;
	int *p;

public:
	TestCls()	//无参构造函数
	{
		std::cout<<"TestCls()"<<std::endl;
		p = new int;
	}
	
	~TestCls()		//析构函数
	{
		delete p;	
		std::cout<<"~TestCls()"<<std::endl;
	}
};

类中的指针p在构造函数中分配的空间,在析构函数中释放。

int main(void)
{
	TestCls t;
	
	return 0;
}

编译运行确实不会出错:
这里写图片描述

类在我们没有定义拷贝构造函数的时候,会默认定义默认拷贝构造函数,也就是说可以直接用同类型的类间可以相互赋值、初始化:

int main(void)
{
	TestCls t1;
	TestCls t2 = t1;   //效果等同于TestCls t2(t1);

	return 0;
}

编译通过,运行却出错了:
这里写图片描述

原因就在于,默认的拷贝构造函数实现的是浅拷贝。

####2. 深度拷贝和浅拷贝
深度拷贝和浅拷贝在c语言中就经常遇到的了,在这里我简单描述。
一般的赋值操作是深度拷贝:

//深度拷贝
int a = 5;
int b = a;

简单的指针指向,则是浅拷贝:

//浅拷贝
int a = 8;
int *p;
p = &a;
char* str1 = "HelloWorld";
char* str2 = str1;

将上面的浅拷贝改为深度拷贝后:

//深度拷贝
int a = 8;
int *p = new int;
*p = a;
char* str1 = "HelloWorld";
int len = strlen(str1);
char *str2 = new char[len];
memcpy(str2, str1, len);

稍微有点c语言基础的人都能看得出深度拷贝和浅拷贝的差异。总而言之,拷贝者和被拷贝者若是同一个地址,则为浅拷贝,反之为深拷贝。
以字符串拷贝为例,浅拷贝后,str1和str2同指向0x123456,不管哪一个指针,对该空间内容的修改都会影响另一个指针。
这里写图片描述

深拷贝后,str1和str2指向不同的内存空间,各自的空间的内容一样。因为空间不同,所以不管哪一个指针,对该空间内容的修改都不会影响另一个指针。
这里写图片描述

####3. 解决默认拷贝构造函数的弊端
类的默认拷贝构造函数只会用被拷贝类的成员的值为拷贝类简单初始化,也就是说二者的p指针指向的内存空间是一致的。以前面TestCls可以知道,编译器为我们默认定义的拷贝构造函数为:

TestCls(const TestCls& testCls)
{
	a = testCls.a;
	p = testCls.p;		//两个类的p指针指向的地址一致。
}

main函数将要退出时,拷贝类t2的析构函数先得到执行,它把自身p指向的堆空间释放了;接下来,t1的析构函数得到调用,被拷贝类t1的析构函数得到调用,它同样要去析构自身的p指向指向的堆空间,但是该空间和t2类中p指向的空间一样,造成重复释放,程序运行崩溃。

解决办法十分简单,自定义拷贝构造函数,里面用深度拷贝的方式为拷贝类初始化:

class TestCls{
public:
	int a;
	int *p;

public:
	TestCls()
	{
		std::cout<<"TestCls()"<<std::endl;
		p = new int;
	}
	

	TestCls(const TestCls& testCls)
	{
		std::cout<<"TestCls(const TestCls& testCls)"<<std::endl;
		a = testCls.a;
		//p = testCls.p;
		p = new int;
	
		*p = *(testCls.p);		//为拷贝类的p指针分配空间,实现深度拷贝
	}
	
	~TestCls()
	{
		delete p;	
		std::cout<<"~TestCls()"<<std::endl;
	}
};

int main(void)
{
	TestCls t1;
	TestCls t2 = t1;

	return 0;
}

编译运行正常:
这里写图片描述

关于c++拷贝构造函数的深度拷贝和浅拷贝的介绍到这里,其实还可以将它们的地址打印出来看看,不过这一步就不再赘述了。

c++的拷贝构造函数还有一处妙用,就是自定义拷贝构造函数,并设置为private属性,其实现体可以什么都不写,那么这个类将变成一个不可被复制的类了。

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

c++的默认拷贝构造函数,从深度拷贝和浅拷贝说起 的相关文章

  • 拷贝构造函数

    拷贝构造函数是重载构造函数的一种重要形式 xff0c 它的功能是使用一种已经存在的对象去初始一个新创建的同类对象 xff0c 它可以将一个已有对象的数据成员的值拷贝给正在创建的另一个同类的对象 拷贝构造函数与类同名 xff0c 没有返回值
  • C++big three(构造函数、拷贝构造函数,拷贝赋值函数)

    一个类中只要带有指针类型的成员 xff0c 就必须自己写出big three xff08 构造函数 拷贝构造函数 xff0c 拷贝赋值函数 xff09 xff0c 如果没有指针类型的成员 xff0c 大部分情况下可以用默认的 字符串类是一个
  • C语言学习之认识exit()函数

    C语言学习之认识exit 函数 在C语言的main函数中我们通常使用return 0 exit 0 表示程序正常退出 exit exit 1 表示程序异常退出 exit 结束当前进程 当前程序 在整个程序中 只要调用 exit 就结束 但在
  • 构造函数属性为protected或者private时

    在c 中 不仅限于c 一个函数被声明为protected或者private时 那也就意味着不能被外部直接调用了 类的成员函数add 是private class cla private int add int a int b return
  • 显式调用构造函数和析构函数

    今天跟同事聊天 他说到STL源码有用到显示调用析构函数 试一了一下 果然能行 include lt iostream gt using namespace std class MyClass public MyClass cout lt l
  • C语言中构造随机数原理及rand()取余构造随机数方法

    在C语言中 ANSIC C程序库提供rand 函数来产生随机数 但事实上 rand 是并不是一个真正的随机数产生器 即可以预测随机序列的顺序 在默认随机种子情况下产生0 99之间的随机数 其随机序列为 83 86 77 15 比如以下程序
  • 链接库介绍

    什么是库 计算机中 有些文件专门用于存储可以重复使用的代码块 例如功能实用的函数或者类 我们通常将它们称为库文件 简称 库 Library 以 C 语言为例 如下展示一个函数库 myMath c int add int a int b re
  • 单链表实现多项式相加

    这个小项目用C语言实现 代码中有我的注释 思路 用链表的每个节点存储表达式的每一项 因此每个链表就是一个表达式 链表节点类型的定义 struct Node DataType elem 项的系数 Variate ch 常量和变量的标志 规定如
  • 2022-12-27 使用lodash库实现两个非空对象的深拷贝并输出这两个对象的并集

    问题描述 遇到这样一个题 如下 const a fruits apple banana series apple C banana A B const b fruits banana orange animals pig series ba
  • Bean深拷贝忽略大小写

    import java lang reflect Field import java util HashMap import java util Map public class BeanMapUtils 忽略大小写且NULL值不会覆盖新值
  • C++ 构造函数和析构函数是否可以继承?

    先看一个例子 cpp view plain copy include
  • unistd.h文件

    转载地址 http baike baidu com link url nEyMMFYevs4yoHgQUs2bcfd5WApHUKx0b1ervi7ulR09YhtqC4txmvL1Ce3FS8xTKtWQuvmEBHC9xezMGpvGH
  • 错误:Visual Studio has encountered a problem and needs to close

    我使用VS2008 Qt4 7 4开发时 安装Qt后报出该错误 google了一下解决了问题 原来在安装QT插件不正确导致的 在360软件管家中 卸载了qt win opensource 4 7 4 vs2008 但qt vs addin
  • 从Qt谈到C++(一):关键字explicit与构造函数

    原文 http blog csdn net guodongxiaren article details 24455653 主题 Qt 提出疑问 当我们新建了一个Qt的widgets应用工程时 会自动生成一个框架 包含了几个文件 其中有个ma
  • Vue - 使用Lodash进行深拷贝

    文章目录 深浅拷贝的理解 使用lodash 深浅拷贝的理解 浅拷贝 只是将数据中所有的数据引用下来 依旧指向同一个存放地址 拷贝之后的数据修改之后 也会影响到原数据的中的对象数据 例如 Object assign 扩展运算符 深拷贝 将数据
  • C/C++基于线程的并发编程(二):线程安全和线程锁

    线程安全 所谓线程安全不是指线程的安全 而是指内存的安全 线程是由进程所承载 所有线程均可访问进程的上下文 意味着所有线程均可访问在进程中的内存空间 这也是线程之间造成问题的潜在原因 当多个线程读取同一片内存空间 变量 对象等 时 不会引起
  • Java对象深拷贝的几种方式

    对象拷贝 项目开发过程中很多时候需要进行对象复制 可是有的时候会发生复制后的对象 在原对象改变后也相应发生改变 这种时候就有问题了 所以很有必要了解对象的深拷贝 以及深拷贝的几种方式 new 对象 手动 new 新的对象 一个属性一个属性的
  • C++:派生类的默认构造函数和拷贝构造函数调用基类构造函数的机制(含程序验证)

    1 如果基类定义了不带参数的默认构造函数 则编译器为派生类自动生成的默认构造函数会调用基类的默认构造函数 2 如果基类定义了拷贝构造函数 则编译器为派生类自动生成的拷贝构造函数同样会调用基类的拷贝构造函数 3 如果基类定义了带参数的构造函数
  • 使用缺省的拷贝构造函数带来的危险性

    我此前另外一篇文章通过类String看拷贝构造函数 赋值函数的作用和区别 对于更深的拷贝构造函数讨论大家可以参见这篇帖子 C 类对象的复制 拷贝构造函数 通过编写类String的拷贝构造函数和赋值函数介绍了一些拷贝构造数 本文着重介绍拷贝构
  • VS2008错误Error spawning 'cmd.exe'的解决方法

    解决方法 In the Options go into Projects and Solutions gt VC Directories page and place this rows SystemRoot System32 System

随机推荐

  • 使用Java写入Excel下拉选择框选项过多不显示问题

    1 问题描述 工作中遇到需要使用Java poi读写Excel文件的问题 因为需求中有要求在写文件时创建下拉选择框 按照传统的直接使用List集合保存下拉选择框的选项 再通过poi本身的方法将选择框的选项添加到下拉框中 一开始编写demo测
  • 【01】花卉识别-基于tensorflow2.3实现

    2021年6月18日重大更新 目前已经退出bug修复之后的tensorflow2 3物体分类代码 大家可以训练自己的数据集 快来试试吧 csdn教程链接 手把手教你用tensorflow2 3训练自己的分类数据集 CSDN博客 b站视频链接
  • APP自动化测试-4. App控件交互

    APP自动化测试 4 App控件交互 文章目录 APP自动化测试 4 App控件交互 前言 一 元素常用的操作方法 二 元素的常用属性 总结 前言 简单介绍元素的操作方法和元素的属性 一 元素常用的操作方法 click 元素点击 send
  • Mybatis使用in传入List的三种方法

    1 非xml方式 使用注解传in 要使用 Select
  • 全国计算机等级考试三级数据库技术(十一)

    第十一章 故障管理 考点分析 在考试中一般情况下会出现在选择题 填空题部分 常考知识点有 1 掌握故障类型及相应的解决方法 2 掌握数据转储与日志文件的相关内容 3 掌握RAID的冗余技术和服务器容错技术 4 熟悉数据库镜像与数据库容灾 1
  • Java 实战项目-- 家庭记账程序

    模拟实现一个基于文本界面的 家庭记账软件 该软件能够记录家庭的收入 支出 并能够打印收支明细表 项目采用分级菜单方式 主菜单如下 具体要求 查询收支明细 菜单1 时 将显示所有的收入 支出名细列表 每次登记收入 菜单2 后 收入的金额应累加
  • 一、C语言初阶:指针

    1 指针 1 1 指针的算术运算 指针移动 int arr 1 2 3 4 5 int p arr int q 移动指针顺序打印 for int i 0 i lt 5 i q p i printf d q printf n 移动指针倒序打印
  •  RedHat 7.2 安装 Zabbix 监控程序详解(适合对linux初级用户)

    目录 RedHat 7 2 安装 Zabbix 监控程序详解 适合对linux初级用户 2020 0927 1 安装环境 查看环境 2 准备yum环境 3 安装LAMP架构 4 zabbix的安装和配置 4 1 第一次出现报错 4 2 第二
  • Rust——Macos安装使用

    进入官网会自动检测当前是什么操作系统 我的是Mac 所以使用官网给的命令安装就可以了 终端输入 curl proto https tlsv1 2 sSf https sh rustup rs sh 安装过程中 最后一步需要输入选择 输入1是
  • 光线追踪

    光追比较流行 其实很多公司也没用到 学习了下 就是反过来进行了 颜色从物体到像素 改为颜色从像素到物体了 碰撞检测就是射线和球之间的三角形关系 根据韦达定理可以判断 不相交 一个值或者两个值 即中学数学 上图
  • windows更改远程桌面端口命令和手动更改方法

    打开命令提示符窗口 按 Win R 快捷键 输入 cmd 然后按 Enter 键 输入以下命令并按 Enter 键 例子 reg add HKLM SYSTEM CurrentControlSet Control Terminal Serv
  • 买彩票能中大奖?用Java盘点常见的概率悖论

    引言 双色球头奖概率与被雷劈中的概率哪个高 3人轮流射击 枪法最差的反而更容易活下来 让我们用Java来探索ta们 悖论1 著名的三门问题 规则描述 你正在参加一个游戏节目 你被要求在三扇门中选择一扇 其中一扇后面有一辆车 其余两扇后面则是
  • C++进阶篇5:字符串查找

    在STL中 字符串查找可以实现多种功能 例如 搜索单个字符 搜索子串 实现前向搜索 后向搜索 分别实现搜索第一个和最后一个满足条件的字符 或子串 要明确的一点是 所有查找find 函数的返回值均是size type类型 即无符号整数类型 该
  • Qt connect信号槽多次定义,会多次触发槽函数

    问题描述 点击Websocket 连接 按钮的时候将open连接到指定的地址 websocket在连接成功后会发出 connected信号 同时我们在初始化的时候就将 connected信号和我们的onconnected 函数建立连接 on
  • ubuntu 进入 recovery mode

    一 选择打开电源时进入固件 F 二 点击Esc按键 三 选择Enter 四 进入如下界面后点击Esc按键 四 选择 Advanced options for Ubuntu 五 选择recovery mode 六 选择root resume
  • 苹果真伪查询_二手MacBook Pro Air等苹果笔记本验货 鉴定 基本方法 流程

    资深果粉 苹果售后兼二手MacBook卖家教你如何鉴定二手Mac 一些基本要点 学会这几点办法 包你不会翻车 一 主板序列号是否与底壳一致 二 屏幕有无坏点亮点 更改纯色背景查看 三 配置要与年代一致 避免买到修改序列号改年份冒充新款的机器
  • RFID叉车纸滑托盘管理应用方案

    1 RFID使用场景和意义 工业送货车单件满载卷烟1600件 按RFID纸滑托盘卷烟24件 托盘 上下两层左右两垛 一个横切面四托盘的装载模式 可装载卷烟64托盘1536件 装载率达96 采用RFID纸滑托盘比传统托盘联运960件的装载率提
  • 解决ctypes.ArgumentError: argument 1: <class ‘TypeError‘>: wrong type

    在python2转换python3时报错 ctypes ArgumentError argument 1
  • 华为无盘服务器,无盘服务器

    无盘服务器 内容精选 换一换 网吧网络系统需要有很高的带宽 要支持大量的数据传输 因此 网吧对网络硬件有较高的要求 网吧网络在设计的时候 需要保证优质的网络传输速度 而且还要考虑到日后的网络升级和维护 下面 易天光通信 ETU LINK 就
  • c++的默认拷贝构造函数,从深度拷贝和浅拷贝说起

    本文收录于微信公众号 LinuxOK ID为 Linux ok 关注公众号第一时间获取更多技术学习文章 1 c 类的默认拷贝构造函数的弊端 c 类的中有两个特殊的构造函数 1 无参构造函数 2 拷贝构造函数 它们的特殊之处在于 1 当类中没