1. 程式人生 > >寫時拷貝(copy on write)

寫時拷貝(copy on write)

寫時拷貝和傳統深拷貝的區別:深拷貝是,每建立一個物件,則開闢一塊空間,不管讀寫

而寫時拷貝是用一塊空間count計數指向同一塊空間指標的數量。

如果只讀不寫,則只需要開闢一次空間。效率很高,記憶體佔用的也少,如果指向同一塊空間的指標需要

修改其內容,則該指標會自己開闢一塊空間用於修改,不會影響原來的空間。

寫時拷貝有兩種方法

————————————————————————————————————————————————————

第一種:類中定義一個成員變數count,拷貝構造或者賦值的時候,讓兩個物件的變數都指向同一塊空間

用count計數管理寫時拷貝


#include <iostream>
using namespace std;

class String
{
public:
    String(char* str = "")
        :_str(new char[strlen(str)]+1)
        , _refCount(new int(1))
    {
        strcpy(_str, str);
    }

    String(const String& str)
        : _str(str._str)
        ,_refCount(str._refCount)
    {
        (*_refCount)++;
    }

    ~String()
    {
        release();
    }

    String& operator= (const String& s)
    {
        if (_str != s._str)
        {
            release();
            _refCount = s._refCount;
            (*_refCount)++;
            _str = s._str;
        }
        return *this;
    }

    void release()
    {
        if ((*--_refCount) == 0)
        {
            delete[] _str;
            delete _refCount;
        }
    }

private:
    char* _str;
    int* _refCount;
};
但是這樣的缺陷比較明顯,每次構造出一個物件,則會有額外的4個位元組被開闢出來計數。會造成記憶體存在很多碎片

所以推薦第二種

———————————————————————————————————————————————————
第二種:

在_str前附帶count的空間,每次開闢_str的空間則會開闢count的空間,共存亡。這樣就會解決記憶體碎片的問題


class String
{
public:
	String(char* str = "")
		:_str(new char[strlen(str)+5])	//多開闢5個位元組來存放count和字串末尾的\0
	{
		*(int*)_str = 1;	//將_str前count初始化為1
		_str = _str+4;	//然後讓_str指向count後的記憶體。要修改count時再返回來呼叫
		strcpy(_str,str);
	}
	String(const String& s)
		:_str(s._str)
	{
		GetCount()++;
	}
	String& operator=(const String& s)	
	{
		if(_str != s._str)
		{
			this->~String();	//如果該物件的_str的count為1則釋放,否則count-1,呼叫
			_str = s._str;		//析構即可
			GetCount()++;
		}
		return *this;
	}
	~String()	//如果count不為1,則不釋放空間, 只將count-1
	{
		if(GetCount()-- == 1)
		{
			delete[] (_str-4);	//釋放時注意,從count處釋放,否則會釋放掉錯誤的空間
			_str = NULL;
		}
	}
	int& GetCount()	//用這個函式來取_str最前面4個位元組的count
	{
		return *((int*)_str-1);
	}
	void print()
	{
		cout<<_str<<endl;
	}
	//當需要改變其_str時,需要檢查count是否為1,也就是是否只有一個物件 指向該_str
	//如果不是,則額外開闢一塊空間用於改變該物件的_str
private:
	char* _str;
};
//測試
int main()
{
	String s("abc");
	String s1(s);
	String s2("cde");
	s1 = s2;

	return 0;
}

析構的時候注意