浅拷贝的缺点:
对类内的成员变量按字节,一个一个的简单拷贝,这就是浅拷贝,对于日期类对象,没有在堆上创建数据的对象,浅拷贝是可行的,但是对于栈对象,需要在在堆上创建数据的对象,我们需要用到深拷贝来完成对象的拷贝,为了实现深拷贝,我们需要自己实现拷贝构造函数
以下面这段代码为例,证明以上发生的错误
class string
{
public:
string(const char* s = "\0")//默认构造初始化
: _size(strlen(s))
,_capacity(_size)
{
_str = new char[_capacity + 1];//这里的+1是为了存放'\0'
}
~string()
{
delete[] _str;//析构释放
}
private:
char* _str;
int _size;
int _capacity;
};
void Test()
{
string s1;
string s2 = s1;
}
上面这段代码没有写深拷贝构造函数以及深拷贝复制重载函数,当对象s1赋值给s2的时候,发生了堆地址浅拷贝,然后程序结束的时候,重复释放了同一个堆上的空间,造成报错。
利用深拷贝解决问题:
为了解决上面的错误,我们提出了深拷贝构造函数和深拷贝复制重载函数,代码模板如下所示:
class string
{
public:
string(const char* s = "\0")
: _size(strlen(s))
,_capacity(_size)
{
_str = new char[_capacity + 1];
strcpy(_str, s);
}
~string()
{
delete[] _str;
}
string(const string& s)
:_size(s._size)
, _capacity(s._capacity)
{
_str = new char[_capacity + 1];
strcpy(_str, s._str);
}
string& operator=(string& s)
{
//if (this != &s)
//{
// //delete[] _str;
// 不管申请空间失败与否,都会把对象的_str给释放掉
// 为了避免这种情况发生,就采用了临时变量的赋值方式
// //_str = new char[s._capacity + 1];
// //strcpy(_str, s._str);
// //_capacity = s._capacity;
// //_size = s._size;
//}
//return *this;
if (this != &s)
{
char* tmp = new char[s._capacity + 1];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
_capacity = s._capacity;
_size = s._size;
}
return *this;
}
private:
char* _str;
int _size;
int _capacity;
};
void Test()
{
string s1;
string s2 = s1;
}
最后生成的s1对象中的_str和s2对象中的_str指向的空间是不一样的,但是它们空间里的内容是一样的。
深拷贝的现代写法:
在深拷贝的传统写法中,需要申请栈上的空间,然后调用strcpy函数将需拷贝的对象的数据拷贝到当前对象刚申请的栈上,那我们细想一下,如果通过调用拷贝构造函数产生一个新的临时对象,然后将该临时对象的成员变量与当前对象的成员变量进行交换,不就可以省去调用strcpy函数,使代码效率提高了嘛?
//深拷贝的传统写法
string(const string& s)
:_str(nullptr)
,_size(s._size)
,_capacity(s._capacity)
{
_str = new char[_capacity + 1];
strcpy(_str, s._str);
}
//深拷贝的现代写法
string(const string& s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string ttmp(s._str);
Swap(ttmp);
}
//赋值重载函数深拷贝
string& operator=(string s)//传参调用拷贝构造
{
Swap(s);
return *this;
}