C++中的浅拷贝与深拷贝

用自定义的String类解释什么是浅拷贝什么是深拷贝。

class String 
{ 
private: 
 char* _str; 
};
浅拷贝是在调用拷贝函数时进行了值拷贝,这样的拷贝看似没有问题,在调用析构函数时会导致内存泄漏,系统奔溃。

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
	String(const char*str="")
		:_str(new char[strlen(str) + 1])
	{
		strcpy(_str, str);
		cout << "String" << endl;
	}
	~String()
	{
		if (_str)
		{
			delete[]_str;
			cout << "~String" << endl;
		}
	}
	char *Getstr()
	{
		return _str;
	}
private:
	char*_str;
};
void TestString()
{
	String s1("hello world!");
	String s2;
	String s3(s1);
	//String s2 = s1;
	cout << s1.Getstr() << endl;
	cout << s3.Getstr() << endl;

}
int main()
{
	TestString();
	system("pause");
	return 0;
}
我们自己编写了构造函数和析构函数,在运行测试用例时出现系统奔溃:

是因为在String s3(s1)时系统调用默认拷贝构造即“浅拷贝”。


所以我们要自己编写拷贝构造与赋值运算符重载,进行深拷贝。

深拷贝是指在拷贝构造时,新开辟一块空间,将_str指向的内容拷贝到这块新的空间里,再将自己的_str指向这块空间。下面是代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
	String(const char*str="")
		:_str(new char[strlen(str) + 1])
	{
		strcpy(_str, str);
		cout << "String" << endl;
	}
	~String()
	{
		if (_str)
		{
			delete[]_str;
			cout << "~String" << endl;
		}
	}
	String(const String& d)
	{
		this->_str = new char[strlen(d._str) + 1];   //this可以省略,+1是因为字符串后还有‘\0’
		strcpy(_str, d._str);
	}

	String& operator=(const String& d)
	{
		if (this != &d)
		{
			delete[]_str;          //首先释放自己空间
			_str = new char[strlen(d._str) + 1];
			strcpy(_str, d._str);
		}
		return *this;
	}
	char *Getstr()
	{
		return _str;
	}
private:
	char*_str;
};
void TestString()
{
	String s1("hello world!");
	String s2;
	String s3(s1);
    s2 = s1;
	cout << s1.Getstr() << endl;
	cout << s3.Getstr() << endl;
	cout << s2.Getstr() << endl;
}

运行结果为:


深拷贝有两种写法,第一种是传统写法,就是我上面这种写法,老老实实开辟新空间,老老实实拷贝字符串;

还有一种是现代写法,相对于传统写法更简洁一点,是让别人开空间拷贝字符串,再将别人的空间与自己交换,坐享渔翁之利。

代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
	String(const char*str="")
		:_str(new char[strlen(str) + 1])
	{
		strcpy(_str, str);
		cout << "String" << endl;
	}
	~String()
	{
		if (_str)
		{
			delete[]_str;
			cout << "~String" << endl;
		}
	}
	String(const String& d)
		:_str(NULL)               //如果tmp置空可以避免释放野指针        
	{
		String tmp(d._str);        //让临时对象tmp去构造与d._str相同的空间,再将tmp与自己交换
		swap(_str, tmp._str);
	}

	String& operator=(const String& d)
	{
		if (this != &d)
		{
			String tmp(d._str);       //赋值首先要做的是释放自己的空间,然后创建一块新空间,赋值为其他对象成员变量
			swap(_str, tmp._str);     //现代写法可以减少释放自己空间这一步骤,因为临时对象tmp在函数调用后自动释放
		}
		return *this;
	}
	char *Getstr()
	{
		return _str;
	}
private:
	char*_str;
};
void TestString()
{
	String s1("hello world!");
	String s2;
	String s3(s1);
    s2 = s1;
	cout << s1.Getstr() << endl;
	cout << s3.Getstr() << endl;
	cout << s2.Getstr() << endl;


}
int main()
{
	TestString();
	system("pause");
	return 0;
}
现代写法中的赋值运算符重载也可以使用对象参数,上面使用的是引用。

String& operator=(String s)
	{
		swap(_str, s._str);
		return *this;
	}
在这种写法中,因为参数是一个临时变量,所以在调用赋值函数时String s就是对象的临时拷贝,可以直接交换,

String s因为是临时变量,在出了作用域后自动销毁,不用自己调用delete函数。










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值