1.浅拷贝
首先我们先来看这段代码,我们用s1给s2进行拷贝构造,此时我们没有自己写拷贝构造,那么编译器会默认生成。
class String
{
public:
String(const char* ptr = "")
{
if (ptr == NULL)
ptr = "";
_ptr = new char[strlen(ptr) + 1];
strcpy(_ptr, ptr);
}
~String()
{
if (_ptr){
delete[] _ptr;
}
}
private:
char* _ptr;
};
void test(){
String s1("hello");
String s2(s1);
}
此时我们运行程序发现会崩掉。为什么呢?
我们查看了一下两个对象的地址,发现是一样的。
在类里面有指针对象的时候,默认的拷贝构造只是进行值拷贝,两个对象公用一块空间,因此在对象销毁的时候就会发生内存违规。
那么上面说的这种就是浅拷贝:也称为位拷贝。编译器只是将对象中的值采用基本类型值复制的方式拷贝过来,如果对象中管理资源,最后就导致多个对象共享一份资源,当一个对象销毁的时候就会将该资源释放掉,但是另一个对象不知道该资源已经释放,以为还有效,继续对资源进行操作,发生访问违规。
那么这种问题我们应该如何解决?采用深拷贝。
2.深拷贝
深拷贝是拷贝对象具体的内容,内存空间会自主分配,拷贝结束后,虽然两个存的值相同,但是地址不同,两个对象互不影响。
此时我们自己写了一个拷贝构造函数,先申请一块相同大小的空间,然后再将内容拷过来。
String(const String& str)
: _str(new char[strlen(str._str) + 1])
{
strcpy(_str, str._str);
}
此时我们看到两个的内存地址不相同,在做析构的时候也不影响。
对于赋值操作符也是一样的道理,我们必须自己来写
//返回值为引用是因为要连续赋值,参数为引用是为了少调用一次拷贝构造 返回值为this指针也是为了连续赋值
String& operator=(const String& str)
{
//不能自己给自己赋值
if (this != &str)
{
//先申请空间
char* tmp = new char[strlen(str._str) + 1];
//将str的内容拷给tmp
strcpy(tmp, str._str);
//将原有的空间释放
delete[] _str;
//再将tmp里面的字符串目标对象
_str = tmp;
return *this;
}
}