【C++】浅拷贝和深拷贝(String类)

深拷贝与浅拷贝

简单的来说,【浅拷贝】是增加了一个指针,指向原来已经存在的内存。而【深拷贝】是增加了一个指针,并新开辟了一块空间

让指针指向这块新开辟的空间。

【浅拷贝】在多个对象指向一块空间的时候,释放一个空间会导致其他对象所使用的空间也被释放了,再次释放便会出现错误

浅拷贝

为了形象化说明什么是深拷贝和浅拷贝,我们就先写一个String类

类里面包含【构造函数】【拷贝构造函数】【赋值运算符重载】,以及【析构函数】【输出操作符“<<”的重载】

class String
{
public:
	String(const char *pStr = "")
	{
		if(NULL == pStr)
		{
			pstr = new char[1];
			*pstr = '\0';
		}
		else
		{
			pstr = new char[strlen(pStr)+1];//加1,某位是'\0'
			strcpy(pstr,pStr);//用拷贝字符串的函数 
		}
	}
	
	String(const String &s)
		:pstr(s.pstr)//浅拷贝的问题,指向同一块空间,可能造成释放的错误 ,这是浅拷贝的缺点 
	{}
	
	String& operator=(const String&s)
	{
		if(this != &s)
		{
			delete[] pstr;//将原来所指向的空间释放 
			pstr = s.pstr;//让pstr重新指向s的pstr所指向的空间(也会导致错误) 
		} 
		return *this;
	}
	
	~String()
	{
		if(NULL != pstr)
		{
			delete[] pstr;//释放指针所指向的内容 
			pstr = NULL;//将指针置为空	
		} 
	} 
	friend ostream&operator<<(ostream & _cout,const String &s)
	{
		_cout<

经过测试之后,在某种情况下是可以正常运行的,在特定情况下是不可以正常的运行的

举一个不能正常运行的例子

int main()
{	
	String s1("sss");
	String s2(s1);
	String s3(NULL);
	s3 = s1;
	cout<
在该例子中,我们有三个String类的对象,s1调用 【构造函数】存入字符"sss"

s2调用【拷贝构造函数】来利用s1进行初始化,s3则用【赋值运算符】来进行初始化

黑框框里输出了三个“sss”

然而!

【C++】浅拷贝和深拷贝(String类)_第1张图片

这是为什么呢?

通过监视,我们发现他们指向的都是同一块空间,因为地址都是【0x01044570】

在让我们看看【析构函数】

	~String()
	{
		if (pstr != NULL)
		{
			delete[] pstr;
			pstr = NULL;
		}
	}
当我们释放s3的时候,可以正常释放

然而当释放s2的时候,由于【s3已经释放过了】,所以s2所指向的这段空间已经不属于s1或者s2了

此时我们调用delete释放的时候,必然会崩溃(毕竟人家本来就不属于你呀)

【C++】浅拷贝和深拷贝(String类)_第2张图片

深拷贝

深拷贝以及深浅拷贝的对比

深拷贝和浅拷贝的不同之处,仅仅在于修改了下【拷贝构造函数】,以及【赋值运算符的重载】

	String(const String &s)
		:pstr(new char[strlen(s.pstr)+1])
	{
		strcpy(pstr,s.pstr); 
	}
	
	String& operator=(const String &s)
	{
		if(this != &s)
		{
			char* tmp = new char[strlen(s.pstr)+1];//动态开辟一个临时变量,然后将pstr指向这一个新的临时变量里
			delete[] pstr;//将原来的空间进行释放
			strcpy(tmp,s.pstr);//将s.pstr里的内容复制到临时变量中
			pstr = tmp;//pstr指向临时变量的这段空间
		}
		return *this;
	}

对比一下浅拷贝的【拷贝构造函数】【赋值运算符重载】

	String(const String &s)
		:pstr(s.pstr)//浅拷贝的问题,指向同一块空间,可能造成释放的错误 ,这是浅拷贝的缺点 
	{}
	
	String& operator=(const String&s)
	{
		if(this != &s)
		{
			delete[] pstr;//将原来所指向的空间释放 
			pstr = s.pstr;//让pstr重新指向s的pstr所指向的空间(也会导致错误) 
		} 
		return *this;
	}

【C++】浅拷贝和深拷贝(String类)_第3张图片

深拷贝完整版

class String
{
public:
	String(const char* pStr = "")
	{	
		cout<<"String()"<

除此之外,我们可以简化一下深拷贝的【拷贝构造函数】【赋值运算符重载】

String(const String& s)
:_ptr(NULL)
{
	String temp(s._ptr);
	std::swap(_ptr, temp._ptr);
}

【拷贝构造函数】里用s定义临时变量,临时变量会自动调用构造函数开辟空间

然后用swap这个函数交换两个变量之间的内容

原来的内容在temp,并且出了【拷贝构造函数】就销毁了,避免了内存泄漏

String& operator=(const String& s)
{
	if (this != &s)
	{
		String temp(s);
		swap(_ptr, temp._ptr);
	}
	return *this;
}
String& operator=(const String& s)
{
	if(this != &s)
	{
		String temp(s._ptr);
		swap(_ptr,temp._ptr);
	}
	return *this;
}
String& operator=(String temp)
{
	swap(_ptr,temp._ptr);
	return *this;
}
【赋值运算符重载】里,也可以用到类似的方法。在第三个方法里,直接传入一个临时变量,连if判断都可以省去了

总结

【浅拷贝】只是增加了一个指针,指向已存在对象的内存。

【深拷贝】是增加了一个指针,并新开辟了一块空间,让指针指向这块新开辟的空间。

【浅拷贝】在多个对象指向一块空间的时候,释放一个空间会导致其他对象所使用的空间也被释放了,再次释放便会出现错误

你可能感兴趣的:(C++)