寫實拷貝

接前兩篇深拷貝與淺拷貝,實現更加方便的寫實拷貝
什麼時候會寫時才拷貝?很顯然,當然是在共享同一塊內存的類發生內容改變時,纔會發生CopyOnWrite。比如string類的[]、=、+=、+、操作符賦值,還有一些string類中諸如insert、replace、append等成員函數,包括類的析構時。修改數據纔會觸發CopyOnWrite。

第一種

引用計數法:

思路:
定義一個int*_pcount,調用一次構造函數,++(*_pcount),調用一次析構函數,--(*_pcount),直至減爲0,調用delete[] 釋放對象,這種方法可以解決淺拷貝多次釋放內存空間帶來的內存泄漏問題。

代碼實現:

//引用計數法
class String
{
public:
    //構造函數
    String(char *str)
        :_str(new char[strlen(str) + 1])
        , _pCount(new int(1))//初始化爲1
    {
        strcpy(_str, str);
    }

    //拷貝構造函數
    //s2(s1)
    String(const String& s)
        : _str(s._str)
        , _pCount(s._pCount)
    {
        ++(*_pCount);

    }

    void Realse()
    {
        if (--(*_pCount)==0)//如果只有一個對象引用這塊空間,則直接釋放,否則計數器--
        {
            delete[] _str;
            delete[] _pCount;
            _pCount = NULL;
            _str = NULL;
        }
    }
    //賦值運算符重載
    //s2=s1
    String& operator=(const String& s)
    {
        if (_str != s._str)
        {
            Realse();
            _str = s._str;
            _pCount = s._pCount;
            ++(*_pCount);
        }
        return *this;
    }

    //析構函數
    ~String()
    {
        Realse();
    }

    void CopyOnWrite()//寫實拷貝
    {
        if ((*_pCount) > 1)
        {
            char *newstr = new char[strlen(_str) + 1];//新建空間
            strcpy(newstr, _str);//直接拷貝
            --(*_pCount);

            _str = newstr;
            _pCount = new int(1);//此時_pCount指向不同空間
        }
    }
    //修改字符串中的字符
    char& operator[](size_t pos)
    {
        CopyOnWrite();
        return _str[pos];
    }
    const char* c_str()
    {
        return _str;
    }
private:
    char *_str;
    int* _pCount;//引用計數
};

void TestCopyOnWrite()
{
    String s1("1234");
    String s2 = s1;
    s1.operator[](2) = 's';
    cout << s1.c_str() << endl;
    cout << s2.c_str() << endl;
}

在監視窗口看:
這裏寫圖片描述

結果爲:
這裏寫圖片描述

第二種

這裏寫圖片描述
代碼實現爲:


class String
{
public:
    String(char *str )
        :_str(new char[strlen(str)+5])
    {
        _str += 4;//從第5個字節開始
        strcpy(_str, str);
        GetRfCount() = 1;//即相當於寫法1的_pCount=1

    }

    //s2(s1)
    String(const String& s)
        :_str(s._str)
    {
        ++GetRfCount();
    }

    //s2=s1
    String& operator=(const String& s)
    {
        if (_str!=s._str)
        {
            if (--GetRfCount() == 0)
            {
                delete[](_str-4);
            }

            _str = s._str;
            ++GetRfCount();
        }
        return *this;
    }

    const char& operator[](size_t pos) const
    {
        return _str[pos];
    }

    void CopyOnWrite()
    {
        if (GetRfCount() > 1)
        {
            --GetRfCount();
            char *tmp = new char[strlen(_str) + 5];
            tmp += 4;//字符串從第五個字節開始存儲

            strcpy(tmp, _str);
            _str = tmp;
            GetRfCount()=1;
        }
    }

    //獲取前四字節值
    int& GetRfCount()
    {
        return *((int*)(_str - 4));
    }

    const char* c_str()
    {
        return _str;
    }

    char& operator[](size_t pos)
    {
        CopyOnWrite();
        return _str[pos];
    }

    ~String()
    {
        if (--GetRfCount() == 0)
        {
            delete[](_str - 4);
        }
    }
private:
    char *_str;
};

//測試部分
void Test4()
{
    String s1("1234");
    String s2(s1);
    String s3("world");
    //cout << s1.c_str() << endl;
    //cout << s1.GetRfCount() << endl;
    //cout << s2.c_str() << endl;
    //cout << s2.GetRfCount() << endl;


    s1 = s3;
    cout<<s1.GetRfCount()<<endl;
    cout<<s2.GetRfCount()<<endl;
    cout<<s3.GetRfCount()<<endl;

    s1.operator[](3) = 'w';
    cout << s1.c_str() << endl;
}

結果爲:
這裏寫圖片描述

最後這裏推薦一篇陳皓的文章,詳解寫實拷貝

發佈了128 篇原創文章 · 獲贊 51 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章