浅拷贝
浅拷贝也称为位拷贝或者值拷贝,所谓的浅拷贝就是当在进行对象的复制时,只是进行对象的数据成员的拷贝,其中默认的拷贝构造函数也是浅拷贝。大多数情况下,使用浅拷贝是没有问题的,但是当出现动态成员,就会出现问题。
浅拷贝的缺陷:浅拷贝对于指针成员不可行。多个对象共用同一块空间,同一内存地址,但是在调用析构函数释放空间的时候,多次调用析构函数,这块空间被释放了多次,此时程序就会崩溃。
写时拷贝(引用计数的拷贝)
写时拷贝使用了“引用计数”的方法,就是用一块空间_refCount计数指向同一块空间的指针的个数。
写时拷贝有两种方法:
1)在类中定义一个成员变量_refCount,拷贝构造或者赋值的时候,让多个指针指向同一个空间,用_refCount计数管理指针的个数。
代码:
class String1
{
public:
String1(const char* str = "")//构造函数
:_str(new char[strlen(str) + 1])
, _refCount(new int(1))
{
strcpy(_str, str);
}
String1(String1& s)//拷贝构造
:_str(s._str)
, _refCount(s._refCount)
{
(*_refCount)++;
}
~String1()//析构
{
if (--(*_refCount) == 0)//当_refCount=1时,说明该空间只属于一个对象,可以被释放了
{
delete[] _str;
delete _refCount;
}
}
//赋值运算符重载分两种情况,s1=s2,两个属于同一块空间,s1=s2,两个分别是两个空间。
//s1=s2 不属于同一块空间
String1& operator =(const String1& s)
{
if (_str != s._str)
{
if (--(*_refCount) == 0)
{
delete[] _str;
delete _refCount;
}
_str = s._str;
_refCount = s._refCount;
*(_refCount)++;
}
return *this;
}
private:
char* _str;
int* _refCount;
};
2)开辟一个空间,前面4个字节为计数器count,剩下的为字符串_str的空间,用法与分开相同。
代码:
String(const char* str = " ")
:_str(new char[strlen(str)+5]
{
strcpy(_str+4,str);
*((int*)_str) = 1;
_str += 4;
}
String(const String & s)
:_str(s._str)
{
(*(int*)(_str-4))++;
}
~String()
{
if(--*((int*)(_str-4))==0)
{
delete[](_str-4);
}
}
String & operator = (const String & s)
{
if(_str != s.s_str)
{
if(--GetRefCount() == 0)
{
delete[] (_str-4);
}
_str = s.s_str;
GetRefCount()++;
}
return *this;
}
int& GetRefCount()
{
return (*(int *)(_str-4));
}
总结
写时拷贝(Copy-On-Write)技术,就是编程界“懒惰行为”——拖延战术的产物。