写时拷贝
系统中默认的拷贝构造函数和赋值运算符重载函数是一种浅拷贝,若类中的成员变量有指针类型,那么多个对象拥有共同的资源,在析构时,会对同一块内存多次释放。此时,我们就需要自己来写拷贝构造函数和赋值运算符重载函数实现深拷贝。但是深拷贝也有缺点,即若资源只是简单共享,不进行写操作,则会使资源浪费。
写时拷贝技术就是在写之前为浅拷贝,而写时为深拷贝。那么使用写时拷贝时,对象的销毁(析构函数)要进行合理的处理。由于多个对象可能共享共享同一块内存单元,所以应该在最后一个对象销毁时,才释放资源。这就涉及到了引用计数。引用计数用来记录拥有该资源的对象的个数,实现方法是在开辟内存时多开辟四个字节作为引用计数域。引用计数域如下图,可在前也可在后,但为了提高效率,我们使用引用计数域在前,数据域在后的方法。
具体实现
class String
{
public:
String(char* ptr)
{
mptr=new char[strlen(ptr)+1+4]();
mptr+=4;
strcpy_s(mptr,strlen(ptr)+1,ptr);
getRef()=1;
}
String(const String& rhs)
{
mptr=rhs.mptr;//浅拷贝
getRef()++;
}
String& operator=(const String& rhs)
{
if(this != &rhs)
{
getRef()--;
if(getRef()==0)
{
delete (mptr-4);
}
mptr=rhs.mptr; //浅拷贝
getRef()++;
}
return *this;
}
~String()
{
--getRef();
if(getRef()==0)
{
delete (mptr-4);
}
mptr=NULL;
}
char& operator[](int index)//实现深拷贝
{
if(getRef()>1)//该内存至少有两个指向
{
char* ptmp=new char[strlen(mptr)+1+4]();
ptmp+=4;
strcpy_s(ptmp,strlen(mptr)+1,mptr);
--getRef();
mptr=ptmp;
getRef()=1;
}
return mptr[index];
}
private:
int& getRef()
{
return *(int*)(mptr-4);
}
char* mptr;
};
int main()
{
String str1("hello");
String str2(str1);
String str3=str1;
str3[0]='b';
return 0;
}
监视窗口中,我们可以看到str1和str2中的成员变量mptr的地址相同,即两个对象共享同一块资源,而str3进行了修改操作,所以str3有自己的资源。