C++ 浅拷贝与深拷贝

完整程序见C++ 浅拷贝 深拷贝

  • 没有经过重载,"=" 的作用就是把左边的变量变得和右边的相等,即执行逐个字节拷贝的工作,对于指针变量,会使得两个指针指向同一个地方,这样的拷贝就叫做“浅拷贝”。
  • 将一个指针变量指向的内容复制到另一个指针变量指向的地方,这样的拷贝就叫做“深拷贝”。
class CString
{
private:
	char* str;
public:
	CString() :str(NULL) {}
	~CString() { if (str) delete[] str; }
	const char* c_str() const { return str; }
	CString& operator=(const char* s)
	{
		char* str_tmp = NULL;

		if (s)
		{
			str_tmp = new char[strlen(s) + 1];
			strcpy(str_tmp, s);
		}
		else
			str_tmp = NULL;

		if (str)
			delete[] str;

		str = str_tmp;

		return *this;
	}
};

按照上面 CString 类的写法,下面的程序片段 "s1 = s2;" 执行的是浅拷贝,会引发问题:

CString s1, s2;
s1 = "this";
s2 = "that";
s1 = s2;

我们可以看到,浅拷贝将导致一系列问题:

  • 如不定义自己的赋值运算符,那么 "s1 = s2;" ,实际上导致 s1.str 和 s2.str 指向同一地方。这导致 s1.str 原来指向的那片动态分配的存储空间再也不会被释放,变成内存垃圾,即内存泄漏。
  • 此外 s1 和 s2 消亡时都会执行 "delete[] str;" ,这就使得同一片存储空间被释放两次,会导致严重的内存错误,可能会引发程序意外中止。
  • 而且,如果执行完 "s1 = s2;" 后又执行 "s1 = "other";" ,会导致 s2.str指向的地方被 delete 释放。

为解决上述问题,需要对 "=" 做再次重载。重载后的 "=" 逻辑,应该是使得执行 "s1 = s2;" 后, s1.str 和 s2.str 依然指向不同的地方,但是这两处地方所储存的内容是一样的。再次重载 "=" 的写法如下:

CString& CString::operator=(const CString& s)		// 深拷贝
{
	// 为了应对 s=s 这样的情况。其实这里的自赋值判断是多余的,因为下面的写法可以处理自赋值的情况,同时还是异常安全的。
	if (this == &s)
		return *this;

	char* str_tmp = NULL;
	if (s.str)
	{
		str_tmp = new char[strlen(s.str) + 1];
		strcpy(str_tmp, s.str);
	}
	else
		str_tmp = NULL;

	if (str)
		delete[] str;

	str = str_tmp;

	return *this;
}

程序第3行要判断 this == &s ,是因为要应付这样的情况:s1=s1 。这句 "s1=s1" 本不该改变 s1 的内容才对。

重载了两次的 "=" 的CString类依然可能导致问题,因为没有重写复制构造函数。比如下面的情况:

CString s21;
s21 = "Transformers";
CString s22(s21);

默认复制构造函数执行的是浅拷贝,还会使得 s21.str 和 s22.str 指向同一个地方。因此,还应该为 CString 类编写如下复制构造函数,以完成深拷贝:

CString::CString(const CString& s)			// 深拷贝
{
	if (s.str)
	{
		str = new char[strlen(s.str) + 1];
		strcpy(str, s.str);
	}
	else
		str = NULL;
}
原文地址:https://www.cnblogs.com/ltimaginea/p/13997408.html