C++写时拷贝
一、什么是写时拷贝
如果后续没有对资源进行修改的操作,甚至不会进行数据拷贝,如果在fork函数返回之后,马上调用exec()函数,也不会有数据拷贝
exec函数的作用是让子进程执行其它程序,即替换当前进程的映像
方案一:
如下图所示:
当创建一个对象s1,再拷贝构造一个对象s2时,引用计数_refCount自动加1,此时为2。若此时s2要对自身进行修改,则s2重新开辟一块空间,检查引用计数_refCount是否大于1,若大于1,,则进行修改,这样就不会影响其他对象。
s2重新开辟一块儿空间,再修改s1原来空间的引用计数_refCount,使其减1,重新开辟的空间中因为此时只有s2一个对象,所以引用计数_refCount也为1.
小结:此方案的写时拷贝计数是同时开辟两块空间,一个自身存放的内容,一个存放引用计数_refCount,同时管理两块空间,统计当前使用此空间的对象数,当要修改当前空间的时候,进行对引用计数的判断,再决定是否开辟新的空间。
代码如下:
class String { public: //构造函数 String(char* str="") :_str(new char[strlen(str)+1]) ,_refCount(new int(1)) { strcpy(_str, str); } //拷贝构造函数 String(String& s) { _str = s._str; _refCount = s._refCount; ++(*_refCount); } String& operator=(const String& s) { if (_str != s._str) { Release(); _str = s._str; _refCount = s._refCount; (*_refCount)++; } return *this; } void Release() { if (--(*_refCount) == 0) { cout << "delete" << endl; delete[] _str; delete _refCount; } } ~String() { Release(); } void CopyOnWrite() { if (*_refCount > 1) { char* tmp = new char[strlen(_str) + 1]; strcpy(tmp, _str); --(*_refCount); _str = tmp; _refCount = new int(1); } } char& operator[](size_t pos) { CopyOnWrite(); return _str[pos]; } char operator[](size_t pos) const { return _str[pos]; } private: char* _str; int* _refCount; };
方案1中是开辟了两块空间进行管理,方案2采用开辟一块空间进行写时拷贝的操作。
开辟一块空间,在这块空间的头4个字节中放置引用计数,真正存放内容的空间从第5个字节开始。一次性多开辟4个字节进行写时拷贝的操作。
具体如下图所示:
当进行操作时,先检查引用计数的个数,然后进行判断是否开辟新的空间,同时修改引用计数的值,防止空间不能释放。
具体例子如下:
当创建了3个对象,s1,s2,s3同时指向一个空间,此时引用计数为3,再创建1个对象s4,s4的引用计数为1。
再进行操作s3 = s4;此时对应的引用计数和对象的指向都需要更改,更改之后如下图所示:
对象s3指向了s4,同时s3原来的空间的引用计数进行减1,新指向空间的引用计数进行加1.
小结:方案2的写时拷贝计数使用一块空间进行内容和引用计数的管理和操作,不开辟两块空间,方便管理。
代码如下:
class String { public: String(const char* str) :_str(new char[strlen(str) + 5]) { _str += 4; strcpy(_str, str); GetRefCount(); } String(const String& s) :_str(s._str) { ++GetRefCount(); } ~String() { if (--GetRefCount() == 0) { cout << "delete" << endl; delete[](_str - 4); } } String& operator=(const String& s) { if (_str != s._str) { if (--GetRefCount() == 0) { delete[](_str - 4); } _str = s._str; GetRefCount()++; } return *this; } int& GetRefCount() { return *((int*)(_str - 4)); } char& operator[](size_t pos) { CopyOnWrite(); return _str[pos]; } void CopyOnWrite() { if (GetRefCount()>1)//当一块空间有两个或者两个以上的对象指向时,才写时拷贝 { char* tmp = new char[strlen(_str) + 5]; strcpy(tmp, _str); --GetRefCount(); _str = tmp; GetRefCount() = 1; } } private: char* _str; int* _refCount; };
以上转载自https://blog.****.net/zy20150613/article/details/76021541