深浅拷贝
拷贝构造函数其实是构造函数的重载,参数类型为(const 类名&形参名),只有一个参数,且必须是引用传参,如果是传值传参,就会引起无限调用
当没有自定义拷贝构造函数的时候,系统就会调用默认的拷贝构造函数 且为浅拷贝(也称位拷贝)
浅拷贝可以实现普通的拷贝构造,但是一旦成员变量中含有指针,则切记不能用默认的拷贝构造(即浅拷贝)
当类的成员变量有指针时,浅拷贝仅仅将新生成的指针和旧指针指向同一个内存单元,不会另开辟空间,因此一旦发生析构,就会出现崩溃
1.因为两个指针指向的是同一内存单元,一旦当一个析构完成后,另一个再析构时,因为找不到所指向的单元,就会发生错误,并导致该指针的内存单元内存泄漏
2.因为指向的是同一个内存单元,一旦一个发生更改,另一个的值也会发生更改
因此深拷贝的作用意义重大
深浅拷贝代码:
class String
{
public:
String(const char* str)
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
String(const String& s) // 1.浅拷贝
:_str(s._str)
{}
String(const String&s){ // 1.深拷贝
_str=new char[strlen(s._str)+1];
strcpy(_str,s._str);
}
String& operator=(const String& s) // 2.浅拷贝
{
if (this != &s)
{
_str = s._str;
}
return *this;
}
String &opreator=(const String&s){ // 2.深拷贝
if(this !=&s){
delete _str; //这一步重新释放并置空再创建是因为在构造新对象时,调用的是默认的构造函数,给_str开辟的大小不一定是合适的,需要重新分配下
_str=NULL;
_str=new char[strlen(s.str)+10];
strcpy(_str,s._str);
}
return *this;
}
~String()
{
if (_str)
{
delete[] _str;
}
_str = NULL;
}
private:
char* _str;
};
引入计数的写时拷贝
写时拷贝主要是综合了浅拷贝和深拷贝,即不会导致复制出来的变量同时指向一个内存空间导致访问出错,又避免了多次深拷贝开辟空间导致增加时耗的现象
写时拷贝的实现原理是通过引入一个 int*_refCount 来对一个内存空间进行监视,先是浅拷贝不考虑同时指向的问题,当该内存空间只被一次指向的时候为1,可直接析构证明无同时指向的现象,当解引用发现是2时,证明此时有两个指针同时指向这片空间,再进行深拷贝开辟空间来避免此问题。
写时拷贝代码:
class String
{
public:
String(char* str="")
:_str(new char[strlen(str)+1])
, _refCount(new int(1)) //引用计数的初始值为1
{
strcpy(_str, str);
}
//拷贝构造 s1(s2),采用浅拷贝
String(const String& s)
:_str(s._str)
, _refCount(s._refCount)
{
++(*_refCount);
}
//s1=s2(先将_str指向空间的引用计数--,然后看那个空间的引用计数是否为0,如果为0,则将该空间释放,然后将s的值和引用计数赋值给_str
String& operator=(const String& s)
{
if (_str != s._str)
{
if (--(*_refCount) == 0)
{
delete[] _str;
delete _refCount;
}
_str = s._str;
_refCount = s._refCount;
++(*_refCount);
}
return *this;
}
~String() //先将自己的引用计数--,然后看该引用计数是否为0,如果为0,则将该空间释放
{
if (--(*_refCount) == 0)
{
delete[] _str;
delete _refCount;
}
}
void CopyOnWrite() //只有当引用计数大于1时才开空间,如果引用计数等于1,说明当前空间只有自己一个对象指向它,直接可以对这个空间进行操作
{
if (*_refCount > 1)
{
char* newstr = new char[strlen(_str) + 1];
strcpy(newstr, _str);
_str = newstr;
_refCount = new int(1);
}
}
private:
char* _str;
int* _refCount;
};