浅拷贝与深拷贝

 

       浅拷贝是指类或结构包含指针类型成员变量,发生拷贝时,Object2=Object1,只是将Object1的变量指针的值赋给Object2,两者指向同一内存,如果Object1修改这块内存数据,Object2的指针变量所指向的数据也会发生变化。如果Object1释放了这块内存而Object2继续使用就可能导致程序出错。
       下列程序所发生的就是浅拷贝

class CopyTest
{
public:
	CopyTest()
	{
		ptr = nullptr;
		memset(arr, 0, 8);
	}
	char* ptr;
	char arr[8];
};
int main()
{
	std::cout << "sizeof(CopyTest)=" << sizeof(CopyTest) << std::endl;//CopyTest所占内存大小为16字节,不同编译器中指针大小不一样,这里是8字节

	CopyTest* pTest1 = new CopyTest;
	pTest1->ptr = new char[20];
	memset(pTest1->ptr, 0, 20);
	//给pTest1的ptr和arr赋值
	memcpy(pTest1->ptr, "hello world!", 12);
	memcpy(pTest1->arr, "1234567", 7);

	std::cout << "sizeof(*pTest1)=" << sizeof(*pTest1) << std::endl;//*pTest1所占内存大小为16字节
	std::cout << std::hex;//以十六进制显示整数

	std::cout << "pTest1:" << std::endl;
	//显示pTest1的ptr和arr的地址和内容
	std::cout << "ptr address(0x" << (void*)pTest1->ptr << ") content=" << pTest1->ptr << std::endl;
	std::cout << "arr address(0x" << (void*)pTest1->arr << ") content=" << pTest1->arr << std::endl;

	CopyTest test2 = *pTest1;
	std::cout << "test2:" << std::endl;
	//显示test2的ptr和arr的地址和内容,由于浅拷贝,test2.ptr与pTest1->ptr相同,包括地址和内容
	std::cout << "ptr address(0x" << (void*)test2.ptr << ") content=" << test2.ptr << std::endl;
	//数组为深拷贝,内容被复制到test2的内存中,地址与pTest1的arr不一样
	std::cout << "arr address(0x" << (void*)test2.arr << ") content=" << test2.arr << std::endl;

	delete[] pTest1->ptr;//释放ptr
	delete pTest1;//释放pTest1

	std::cout << "test2:" << std::endl;
	//显示test2的ptr和arr的地址和内容,到这里test2.ptr已经被pTest1释放,变为无效地址,打印出乱码
	std::cout << "ptr address(0x" << (void*)test2.ptr << ") content=" << test2.ptr << std::endl;
	//test2.arr仍然可用
	std::cout << "arr address(0x" << (void*)test2.arr << ") content=" << test2.arr << std::endl;

	std::cout << std::dec;//重置为10进制显示

	system("pause");
	return 0;
}

运行结果:

       默认拷贝构造函数(test2=*pTest1)只是将*pTest1的内存数据拷贝到test2,相当于memcpy,*pTest1内存中只包含一个指针值ptr和一个8字节的数组arr,并不包含"hello world!"字符串, 因此两者ptr指向同一块内存,无论谁修改两者都会受影响,而arr则各自有一份数据,互不影响。
       delete[] pTest1->ptr;调用之后test2的ptr也会被释放,所以打印出来是随机的字符串(一般是乱码,如果这块内存没有被其他程序修改,也有可能打印正常,但是不能再写入数据)。
       通常我们会在类的析构函数中释放成员变量(定义了析构函数之后要将delete[] pTest1->ptr这一行注释掉。),如下所示:

class CopyTest
{
public:
	CopyTest()
	{
		ptr = nullptr;
		memset(arr, 0, 8);
	}
	virtual ~CopyTest()//析构函数一般定义为虚函数
	{
		if (ptr)
		{
			delete[] ptr;//假定我们分配的始终是char数组
			ptr = nullptr;
		}
	}
	char* ptr;
	char arr[8];
};

 

       这样再重新跑一遍程序就会出错,原因是delete pTest1后ptr就已经被释放,main函数结束之后会调用test2的析构函数,test2又会对ptr调用delete[] ptr,对无效的地址再次释放会导致程序出错。

       这个问题的一个解决办法就是再定义一个拷贝构造函数,将pTest1的ptr所指向的内存也拷贝一份到新的内存,使test2的ptr指向这块内存,这样两个对象实例的ptr相互独立,彼此不受影响,这也就是深拷贝。再次修改类结构如下:

class CopyTest
{
public:
	CopyTest()
	{
		ptr = nullptr;
		memset(arr, 0, 8);
	}
	CopyTest(const CopyTest& InOther)
	{
		int len = strlen(InOther.ptr);//假定是字符串,否则长度值要由其他方法获取
		ptr = new char[len + 1];
		memcpy(ptr,InOther.ptr,len);
		ptr[len] = '\0';
		memcpy(arr, InOther.arr,8);
	}
	virtual ~CopyTest()//析构函数一般定义为虚函数
	{
		if (ptr)
		{
			delete[] ptr;//假定我们分配的是char数组
			ptr = nullptr;
		}
	}
	char* ptr;
	char arr[8];
};

运行后程序就正常了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值