再议深拷贝与浅拷贝

时间:2021-11-16 21:27:02

今天做了一道腾讯的笔试题,与大家一起分享一下:

再议深拷贝与浅拷贝

早在博文《浅拷贝和深拷贝的区别?》一文中就讲到要特别注意类中含有指针数据成员的情况,在对类进行拷贝操作时,如果没有在类中自己定义拷贝构造函数,编译器会自动调用默认拷贝构造函数,即把某个对象的数据成员简单的复制给另一个对象,如果类含有指针数据成员,这种简单的浅拷贝会导致两个对象的指针数据成员都指向同一片内存,只要其中一个对象操作该片内存,另一个对象也会随之改变。

有以下三种情况会自动调用默认拷贝构造函数:

1、在类中新建一个拷贝对象,有类名 对象2(对象1)和 类名 对象2 = 对象1 两种形式。

2、当函数参数为类的对象时,即上题出现的情况。

3、当函数的返回值是类的对象时,此时也需要将函数中的对象复制给一个临时对象并传给该函数的调用处。

为什么要特别注意这三种情况呢?因为程序结束时会调用两次析构函数,在上一题中sayHello函数在传入对象b时首先会进行浅拷贝操作,即:

B(const B& ths)
{
     p = ths.p;
}
临时对象在函数调用结束时会调用一次析构函数,整个程序结束时(对象b销毁)又会调用一次析构函数,即对指针p进行两次delete操作,重复销毁指针p会导致内存泄漏、程序崩溃。


所以,怎样解决浅拷贝类指针数据成员导致的内存泄漏呢?——自己定义拷贝构造函数,为每个指针数据成员分配不同的内存空间,即深拷贝。

具体到题目:

class A
{
	int i;
};

class B
{
	A *p;
public:	
	B(){ p = new A; }
	~B(){ delete p; }
	B(const B& newb)
	{
		p = new A;
		*p = *(newb.p);
	}
};

void sayHello(B b)
{}

int main(void)
{
	B b;
	sayHello(b);

	return 0;
}