浅拷贝是值拷贝,由之前深拷贝分析的文章知,浅拷贝后,两个指针指向同一空间。容易出现多次释放同一空间的问题,以及修改其中一个指针指向的内容影响其他指针指向的内容的问题。而深拷贝效率低,每次对对象进行值和空间同时拷贝,但这样会存开辟很多空间。为了避免产生更多的空间,引入写时拷贝,当对空间进行更改时,检查是否有除了自己以外的对象使用该块空间,若有,则自己重新开辟空间进行改正,不影响其他对象;若没有其他对象使用此空间,则说明只有自己使用此空间,直接进行更改。此时引用计数用来统计有多少对象使用此空间,克服了浅拷贝和深拷贝存在的问题。
void TestString()
{
String s1;
for(size_t i=0;i<1000000;i++)
{
String s2(s1);//此处的拷贝构造为深拷贝时,程序会崩溃
}
}
一、分析写时拷贝。
1、引用计数的工作方式
1)除了初始化对象外,每个构造函数(拷贝构造函数除外)要建立一个引用计数,用来记录多少对象与正在创建的对象为共享一块内存空间。当我们创建一个对象时,只有一个对象用该空间,故将引用计数初始化为1。
2)拷贝构造函数不分配新的计数器,而是拷贝数据成员,包括计数器。拷贝构造递增计数器,指出给定的对象的内存空间被一个新的用户共用。
3)析构函数递减计数器,指出共用空间的用户少了一个,如果计数器为0,则释放该空间。
4)赋值运算符重载,递增右侧运算对象的计数器,递减左侧运算对象的计数器。如果左侧运算对象的计数器为0,就要对该对象进行销毁。
2、写时拷贝
写时拷贝在写的时候(即改变对象内容时)才会真正的开辟空间拷贝(深拷贝),如果只是对数据进行读,只会对数据进行浅拷贝。
写时拷贝:引用计数器的浅拷贝,又称延时拷贝,写时拷贝技术是通过“引用计数”实现的,在分配空间的时候多分配4或8个字节,用来记录有多少指针指向该空间,当有新指针指向这块空间时,引用计数加1。当要释放该块空间时,引用计数减1,直到引用计数减为0才释放掉该块空间。当有的指针要改变这块空间的值时,再为这个指针分配自己的空间(此时,引用计数的变化,旧空间的引用计数减1,新分配的空间的引用计数加1)。
二、完成写时拷贝的各种方案。
1、分析以下String的copy on write的几种实现方案哪种可以?为什么?
//方案1
class string
{
private:
char* _str;
int _refcount;
}
//方案1实现
class String
{
public:
String(char* str=" ")
:_refCount(0)
{
_str=new char[strlen(str)+1];
strcpy(_str,str);
_refCount++;
}
String(String &s)
:_refCount(s._refCount)
{
_str=s._str;
_refCount++;
s._refCount=_refCount;
//这里虽然可以让两个对象的_refCount相等,但
//如果超出两个对象的_str指针都指向同一块内存时,
//就无法让所有对象的