(7)C++拷贝构造(浅拷贝、深拷贝)


拷贝构造


注意:拷贝构造不会申请空间
本质:构造函数(与其他构造函数是重载关系)
1.形式

	Cstu(const Cstu &a)//参数是类的常引用
	{

	}

2.原理:
用已有对象给新对象初始化的方式(只有这几种方式才会调用拷贝构造)
新建一个对象,并将其初始化为同类现有对象(用一个对象给新的对象初始化)

3.拷贝构造的调用情况
(1)把对象作为参数进行初始化时会调用
//下面代码是将对象stu1通过传参赋值给a

class Cstu
{
public:
	Cstu()
	{

	}
	//拷贝构造	
	Cstu(const Cstu &a)//参数是类名的引用
	{

	}
};
//主函数调用
//创建新的对象 用stu1作为参数传递
Cstu stu2(stu1);  

(2)通过对象赋值进行初始化时会调用

//街上
//主函数调用
Cstu stu2 = stu1;//赋值传递

(3)通过临时对象赋值(比(2)多了一步创建一个临时对象)时会调用

//同上
//主函数调用
Cstu stu2 = Cstu(stu1);//通过临时对象进行赋值

(4)利用指针对象赋值初始化时会调用


//主函数调用
	Cstu*p = new Cstu(stu1);	//利用指针对象赋值
	delete p;

过程:已有对象遍历所有成员除拷贝构造,新对象只遍历拷贝构造,把已有对象进行拷贝。

(5)当程序生成对象副本时:
作为参数(初始化的原因)
a.形参是局部变量或者局部对象,是局部的,作用域是函数内部
b.解析过程
函数传stu这个对象的参数,传给新对象a,即是用stu给a初始化了 ,所以会调用拷贝构造
c.形参在调用的时候才会分配空间,而不是在编译时

class Cstu
{
public:
	Cstu(const Cstu &a)//参数是类名的引用
	{
		cout << "拷贝构造" << endl;
	}
};
	void fun(Cstu a)
	{

	}
	int main()
{
	Cstu stu1;
	fun(stu1);
	system("pause");
	return 0;
}

作为返回值(临时对象或临时变量的原因)

class Cstu
{
public:
	Cstu(const Cstu &a)//参数是类引用
	{
		cout << "拷贝构造" << endl;
	}
};
	Cstu fun() 
	{
		Cstu a;
		return  a; //若返回一个值,则返回的是临时对象或临时变量
	}

解析:
上面的函数返回的是临时对象,假如返回的是整形12,那么返回的就是int (12);同理,假如返回的是对象,然后就用a给临时对象初始化,所以会调用拷贝构造

特殊情况(赋值不会调用):
而赋值是不会经过拷贝构造的。

//主函数调用
	Cstu stu1;
	Cstu stu2;
	stu2 = stu1;

4.默认拷贝构造(又叫浅拷贝)

a.基于的特性是同一个类的多个对象,对象内存排布是一样的,对象地址可能不同

b.与拷贝构造的区别
默认的拷贝构造什么都不执行,是一个NULL,拷贝构造是执行内容的
总代码如下://结果为abc abc

#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
using namespace std;
class Cstu
{
public:
	char str[4];
	int  b ;
	Cstu()
	{
		str[0] = 'c';
		b = 12;
		strcpy(&str[0],"abc");
	}
	Cstu(const Cstu& a)
	{
		this->b = a.b;  //将拷贝的过程写在表面上
		strcpy(this->str ,a.str );
	}
};
int main()
{
	Cstu stu1;
	cout << stu1.str <<' ';
	Cstu stu2 = stu1;
	cout << stu2.str << endl;
	system("pause");
	return 0;
}

c.功能
默认拷贝构造函数,会逐个复制非静态成员(成员赋值拷贝为浅复制/浅拷贝,复制的是成员的值)值,仅是值的变化,与strcpy类似

//strcpy(目标字符串首地址,源字符串); 一般会报错,要求使用下面的
//strcpy(目标字符串首地址,字节数,源字节数);安全性更高 
class Cstu
{
public:
	char a[4];
	int b = 0;
	Cstu()
	{
		a[0] = 'c';
		b = 12;
	}
	void print()
	{
		strcpy(&a[0],"abc");
		for (int i = 0; i < 4; i++)
		{
			cout << a[i] << ' ' ;
		}
	}

};
//主函数调用
	Cstu stu;
	stu.print();
	Cstu stu1 = stu; //会发现直接执行print()函数,不进Cstu()
					 //说明进了默认的拷贝构造函数
	stu1.print();

深拷贝(有指针成员用)


写在前面:只针对(1)(2)(3)(4)上面4种情况拷贝时的方法
指针变量不是全部复制,若定义int*p = &a;指针仅仅记录的是首地址,当它拷贝的时候,只是会把它装的地址拷贝过去
数组类型和指针类型都是变量,类型不同。
数组类型:假如int a[4],则a的类型是 int[ ]
指针类型:假如int*p,则p的类型是int*
浅拷贝在关于指针成员存在的重复释放空间的问题,演示如下
使用指针成员:在构造函数分配空间 ,在析构函数里释放空间

#include<iostream>
using namespace std;
class Cstu
{
public:
	int *a;
	Cstu()
	{
		a = new int[2];//指针成员在构造函数申请空间
		a[0] = 1;
		a[1] = 2;
	}
	~Cstu()
	{
		delete [2] a;
	}
};
int main()
{
	{
		Cstu stu;
		cout << stu.a[0] << ' ' << stu.a[1] << endl;
		Cstu stu1 = stu;
		cout << stu1.a[0] << ' ' << stu1.a[1] << endl;
	} //加上花括号,析构函数在此调用
	system("pause");
	return 0;
}

错误解析:析构函数每次结束时都会调用
上面代码在stu1=stu走完拷贝构造后,再走析构函数,会使同一块空间重复释放,第二次释放时,此时空间已归系统所有,而指针便成了野指针,所以就会造成系统崩溃。

深拷贝过程

	Cstu(const Cstu& b)
	{
		//传递内容,首先要申请空间,让b有自己的空间
		this->a = new int [2];
	}

原来的对象为stu,新对象为stu1
stu有一段空间,stu1进到拷贝没有空间就指向stu的空间,导致他俩指向了同一块空间,对同一块空间进行多次释放,就崩溃了
深拷贝就是不让stu1指向stu的空间了,让它自己有一块空间,那就是给stu1在拷贝构造里申请空间,把stu里的全部内容复制过来(这里不单单是一个值的传递,一定是把内容也传递过来),复制到stu1申请的新空间来,这样stu1就有自己的空间了,在调用两次析构时,stu释放的是它的空间,stu1释放他的新空间

有指针成员可以用拷贝构造的代码如下:

#include<iostream>
using namespace std;
class Cstu
{
public:
	int *a;
	Cstu()
	{
		a = new int[2];//指针成员在构造函数申请空间
		a[0] = 1;
		a[1] = 2;
	}
	Cstu(const Cstu& b)
	{
		//传递需要释放空间的内容,首先要申请空间
		this->a = new int [2];
		this->a[0] = b.a[0];//给新空间的数组赋值
		this->a[1] = b.a[1];

	}
	~Cstu()
	{
		delete[2] a;  //指针成员在析构函数释放空间
	}
};
int main()
{
	{
		Cstu stu;
		cout << stu.a[0] << ' ' << stu.a[1] << endl;
		Cstu stu1 = stu;
		cout << stu1.a[0] << ' ' << stu1.a[1] << endl;
	} //加上花括号,析构函数在此调用
	system("pause");
	return 0;
}

如上赋值的元素多的话,就可以用内存赋值函数了(将一块空间的内容赋值到另一块空间)
memcpy(目标空间,源空间,空间大小字节数)

Cstu(const Cstu& b)
{
	this->a = new int [2]; 
	memcpy(this->a ,b.a ,8);
}

当对象作为函数参数或返回值时的解决办法
作为函参时:
可以利用传引用或指针,直接指向。中间没产生任何对象赋值初始化的过程,就不会去调用拷贝构造

//类外声明定义
	Cstu& westrong(Cstu&a) //传引用,避免拷贝构造
	{
		return a;
	}
	//主函数调用
		Cstu stu;
		Cstu b = westrong(stu);

作为返回值时:
也可以利用引用或指针,就不产生临时对象了,传递的就是a的本身了。不调用拷贝构造,效率也提高了。

//传指针时 类外定义
Cstu*west(Cstu*a)
{
	return a;
}
//主函数调用
west(&stu);
Cstu* c = west(&stu);
cout << c->a [1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沐鑫本鑫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值