详解拷贝构造

拷贝构造的功能

写法:

拷贝构造函数的参数为什么是引用类型

系统自动生成的拷贝构造函数

拷贝构造的深拷贝与浅拷贝

概念

浅拷贝:

深拷贝

小结


拷贝构造的功能

拷贝构造函数 可以把曾经实例化好的对象的数据 拷贝给新创建的数据,可见说拷贝构造函数在功能上是构造函数的另一种形式。都是让对象初始化。

写法:

拷贝构造函数在语法层是构造函数的重载,函数名就是类名,无返回值,参数是该类类型对象的引用(为了保护被引用对象数据不被破坏,会在类型前面加上const)

cpp 复制代码
 Date(const Date& d) //以日期类为例,定义拷贝构造的写法
cpp 复制代码
Date d1;
 Date d2(d1); //以日期类为例,实例化对象时的写法

拷贝构造函数的参数为什么是引用类型

下面是定义的日期类的拷贝构造函数,但参数并不是引用类型

cpp 复制代码
Date(const Date d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

因为是传值,实例化对象时需要调用拷贝构造函数把数据拷贝过来,但拷贝数据函数的参数又需要掉用拷贝构造函数 ,如此就死循环了,拷贝构造函数会被无限调用。如图所示这种情况编译器会强制报错

系统自动生成的拷贝构造函数

若未显式定义,编译器会生成默认的拷贝构造函数 默认的拷贝构造函数对象按内存存储 按字节序 完成拷贝,这种拷贝叫做浅拷贝 ,或者值拷贝
下面代码中定义了一个时间类 Time ,又定义了一个日期类 Date , 时间类在日期类里实例化了对象_t,_t就是日期类的自定义类型,但并没有定义日期类的拷贝构造函数。那么系统自动生成的拷贝构造函数会怎么处理自定义类型_t和内置类型的年月日呢?

cpp 复制代码
class Time {   //时间类
public:

Time() 
   {
_hour = 1;
_minute = 1;
_second = 1;
}

Time(const Time& t) //自定义时间类的拷贝构造函数
{
_hour = t._hour;
_minute = t._minute;
_second = t._second;
}

private:

int _hour;
int _minute;
int _second;
};

class Date  //日期类,但没有自定义拷贝构造函数
{
	
private:
// 基本类型(内置类型)
	int _year;
	int _month;
	int _day;

// 自定义类型
Time _t;  //时间类实例化对象 _t
	
};

int main(){

Date d1;
Date d2(d1); //拷贝对象d1的数据

 // 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数
    // 但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构造函数

return 0;
}

只需明晰一点即可,系统自动生成的默认拷贝构造对内置类型进行浅拷贝,对自定义类型,会调用自定义类型的拷贝构造函数,若自定义类型并未定义拷贝构造函数,系统便会自动生成。

下图是代码执行示意图

下图是逻辑示意图

拷贝构造的深拷贝与浅拷贝

概念

浅拷贝:

又称值拷贝,是按字节序拷贝的,通俗的讲别的数据长啥样就拷贝啥样。

浅拷贝的细节:下面代码中还是定义了一个日期类,但增加了一个int*的变量它指向一块空间。这是让默认构造函数进行浅拷贝会发生什么呢?

cpp 复制代码
class Date //日期类,并没有定义拷贝构造函数
{
public:

	Date()//构造函数
	{
		_year = 1;
		_month = 1;
		_day = 1;
			_a = (int*)malloc(sizeof(int) * 7); //开辟空间并把空间的值初始化
			for (int i = 0; i < 7; i++)
			{
				_a[i] = 0;
			}
	}

private:
	int _year;
	int _month;
	int _day;
	int* _a; //指向一块空间
};
int main()
{
	Date d; 
	Date d1(d); //拷贝d的值
}

结果:d对象和d1对象的数据会一模一样,如下图

我们发现d对象的值和d1对象的值一模一样,并且d对象中的_a和d1对象中的_a都指向同一块空间。如下图

上述就值浅拷贝的坏处,d1对象的_a并没有申请资源而是直接指向了d对象_a的空间,如果想让d1

_a也申请空间就需要深拷贝,想要深拷贝就需要自己定义一个拷贝构造函数,该拷贝构造函数如何申请空间要根据不同的场景具体实现。

深拷贝

下面的代码是对上面代码的改造,

cpp 复制代码
class Date
{
public:

	Date() //构造函数
	{
		_year = 1;
		_month = 1;
		_day = 1;
			_a = (int*)malloc(sizeof(int) * 7);
			for (int i = 0; i < 7; i++)
			{
				_a[i] = 0;
			}
	}

	Date(const Date& d) //拷贝构造函数
	{
		_year = d._year; 
		_month = d._month;
		_day = d._day;

		_a = (int*)malloc(sizeof(int) * (sizeof(d._a) / sizeof(d._a[0])));  //开辟空间

			for (int i = 0; i < (sizeof(d._a) / sizeof(d._a[0])); i++)  //赋值
			{
				_a[i] = d._a[i];
			}
	}

private:
	int _year;
	int _month;
	int _day;
	int* _a;
};
int main()
{
	Date d;
	Date d1(d);
}

d1对象的_a会独自开空间并且拷贝d对象的数据,如下图这样就完成了拷贝构造的深拷贝了。

小结

拷贝构造是构造函数的重载 。参数是该类类型的引用 (一般会加上const),不加引用在逻辑是会让拷贝函数无限调用,此时编译器会报错。如果未显示定义拷贝构造函数,编译器会生成默认的拷贝构造函数**。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝** ,或者值拷贝。浅拷贝又称值拷贝,是按字节序拷贝的,通俗的讲别的数据长啥样就拷贝啥样。而深拷贝会申请资源(空间)再赋值。

另外我还为大家准备了几篇不错的博客

构造函数: http://t.csdnimg.cn/MdHiA

希尔排序: http://t.csdnimg.cn/uxSBG

堆排序的时间复杂度: http://t.csdnimg.cn/OJLzI

相关推荐
此生只爱蛋13 分钟前
【手撕排序2】快速排序
c语言·c++·算法·排序算法
blammmp20 分钟前
Java:数据结构-枚举
java·开发语言·数据结构
何曾参静谧32 分钟前
「C/C++」C/C++ 指针篇 之 指针运算
c语言·开发语言·c++
暗黑起源喵38 分钟前
设计模式-工厂设计模式
java·开发语言·设计模式
WaaTong43 分钟前
Java反射
java·开发语言·反射
咕咕吖44 分钟前
对称二叉树(力扣101)
算法·leetcode·职场和发展
Troc_wangpeng44 分钟前
R language 关于二维平面直角坐标系的制作
开发语言·机器学习
王哲晓1 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
努力的家伙是不讨厌的1 小时前
解析json导出csv或者直接入库
开发语言·python·json
理想不理想v1 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试