日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

浅拷贝+引用计数--写时拷贝---模拟实现string容器

發布時間:2023/11/30 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浅拷贝+引用计数--写时拷贝---模拟实现string容器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

引用計數

深拷貝
多個對象共享同一份資源時,最后能夠保證該資源只被釋放一次
應該由哪個對象釋放資源?
由最后一個使用該資源的對象去釋放
怎么知道一個對象是最后一個使用該資源的對象?
給一個計數,記錄使用該資源對象的個數

實現

計數用普通整型

先來看一個例子

class string { public:string(char *str = ""){//如果指針為空,則初始化位空字符串if (nullptr == str)str = "";//申請空間_str = new char[strlen(str) + 1];//初始化一個對象占一個資源,引用計數+1_count = 1;//拷貝數據strcpy(_str, str);}string( string& s):_str(s._str), _count(++s._count){}string& operator=(const string& s){//自己給自己賦值,不用做任何操作if (this != &s){}return *this;}~string(){//每次釋放對象計數都要減一,減完之后要看_count是不是0if (_str && 0 == --_count)delete[]_str;_str = nullptr;}//編譯器生成的默認賦值運算符重載存在淺拷貝,而且會有資源泄露,沒有釋放資源 private:char * _str;int _count; };

在類中增加一個變量記錄使用資源的對象數
在類中增加int類型的成員變量----不行,因為這種變量每個對象都存在一份
普通的成員變量,每個對象都有一份,一個對象在修改計數時,不會影響其他對象
導致:資源沒有釋放而引起內存泄露

將計數變為靜態成員變量

class string { public:string(char *str = ""){//如果指針為空,則初始化位空字符串if (nullptr == str)str = "";//申請空間_str = new char[strlen(str) + 1];//初始化一個對象占一個資源,引用計數+1_count = 1;//拷貝數據strcpy(_str, str);}string(string& s) //靜態成員變量不能再在初始化列表中使用:_str(s._str){++_count;}string& operator=(const string& s){//自己給自己賦值,不用做任何操作if (this != &s){}return *this;}~string(){//每次釋放對象計數都要減一,減完之后要看_count是不是0if (_str && 0 == --_count)delete[]_str;_str = nullptr;}//編譯器生成的默認賦值運算符重載存在淺拷貝,而且會有資源泄露,沒有釋放資源 private:char * _str;static int _count; };int string::_count = 0;

將計數給成靜態類型成員變量----不行
靜態類型成員是所有對象共享,計數應該與資源個數保持一致,有多少資源就要要多少計數

計數為整型指針類型


一個對象修改,另外一個對象也能看見

class string{public:string(char *str = ""):_pCount(new int (1)){//如果指針為空,則初始化位空字符串if (nullptr == str)str = "";//申請空間_str = new char[strlen(str) + 1];//拷貝數據strcpy(_str, str);}string(string& s) //靜態成員變量不能再在初始化列表中使用:_str(s._str) //兩個對象共用同一份資源, _pCount(s._pCount) //兩個對象共用一個計數{++(*_pCount);}//s2 = s1//s2原來的資源將不再使用---應該給原來的計數-1// 計數非0:// 計數為0: 釋放掉原來的資源//s2應該與s1共享同一份資源:計數++string& operator=(const string& s){//自己給自己賦值,不用做任何操作if (this != &s){//讓當前對象與其管理的資源分離開if (0 == --*_pCount){delete[]_str;delete _pCount;}//與s共享資源_str = s._str;_pCount = s._pCount;++ (*_pCount);}return *this;}~string(){//每次釋放對象計數都要減一,減完之后要看_count是不是0if (_str && 0 == -- *_pCount){delete[]_str;_str = nullptr;delete _pCount;_pCount = nullptr;}}private:char * _str;int* _pCount;};

引用計數也有缺陷
如果出現這種情況

void TestString() {bite::string s1("hello");bite::string s2(s1);bite::string s3("world");bite::string s4(s3);s3 = s1; //s3不需要釋放原來的資源,因為還有s4在用s1 = s4; //s4是最后使用資源的對象,所以需要釋放 }

這種情況程序走到末尾,4個對象共用同一塊空間,如果用[]運算符去修改對象s1的值,那么其他對象也都被修改

寫時拷貝

所有對象共享一份資源時,讀數據不用拷貝,一但有對象要修改,則單獨為該對象拷貝一份資源
所以當出現所有寫操作或者可能會引起寫操作的方法,都會把當前對象修改掉,所以要分離對象’

namespace bite {class string{public:string(char *str = ""):_pCount(new int (1)){//如果指針為空,則初始化位空字符串if (nullptr == str)str = "";//申請空間_str = new char[strlen(str) + 1];//拷貝數據strcpy(_str, str);}string(string& s) //靜態成員變量不能再在初始化列表中使用:_str(s._str) //兩個對象共用同一份資源, _pCount(s._pCount) //兩個對象共用一個計數{++(*_pCount);}//s2 = s1//s2原來的資源將不再使用---應該給原來的計數-1// 計數非0:// 計數為0: 釋放掉原來的資源//s2應該與s1共享同一份資源:計數++string& operator=(const string& s){//自己給自己賦值,不用做任何操作if (this != &s){//讓當前對象與其管理的資源分離開if (0 == --*_pCount){delete[]_str;delete _pCount;}//與s共享資源_str = s._str;_pCount = s._pCount;++ (*_pCount);}return *this;}char& operator[](size_t index){//該操作可能會改變當前對象的內容//必須:分離當前對象if (GetRef() > 1){string strtemp(_str);//構造臨時對象this->swap(strtemp); //當前對象與臨時對象交換}return _str[index];}~string(){//每次釋放對象計數都要減一,減完之后要看_count是不是0if (_str && 0 == -- *_pCount){delete[]_str;_str = nullptr;delete _pCount;_pCount = nullptr;}}void swap(string &s){std::swap(_str, s._str);std::swap(_pCount, s._pCount);}private://獲取引用計數int GetRef(){return *_pCount;}private:char * _str;int* _pCount;}; }void TestString() {bite::string s1("hello");bite::string s2(s1);bite::string s3("world");bite::string s4(s3);s3 = s1; //s3不需要釋放原來的資源,因為還有s4在用s1 = s4; //s4是最后使用資源的對象,所以需要釋放s1[0] = 'H';char& rc = s1[0];rc = 'H'; }


寫時拷貝單線程底下沒有問題,但在多線程下可能會出錯

~string(){//每次釋放對象計數都要減一,減完之后要看_count是不是0if (_str && 0 == -- *_pCount){delete[]_str;_str = nullptr;delete _pCount;_pCount = nullptr;}}

線程1計數減過了但是時間片到了,還沒來的及與0比較。線程2過來,發現資源還存在,而且線程2時間片充足,就會去釋放資源。釋放完后,線程1又開始執行,發現計數已經變為0,就會把資源再釋放一次,也會造成代碼崩潰

模擬實現string

namespace bite {class string{public:typedef char* iterator;public:string(const char* str = ""){if (str == nullptr)str = "";//當前對象開辟空間_size = strlen(str);_capacity = _size ;_str = new char[_capacity + 1];//拷貝元素strcpy(_str, str);}//放入n個字符chstring(size_t n, char ch):_size(n), _capacity(n), _str(new char[n + 1])//此處不能new char[_capacity],因為成員變量初始化,只跟聲明順序有關,_str先于_capacity聲明,所以//先初始化{memset(_str, ch, n);_str[n] = '\0'; //最后一個位置設置為\0}//[begin,end)string(char* begin, char* end){_size = end - begin;_capacity = _size;_str = new char[_size + 1];strncpy(_str, begin, _size);_str[_size] = '\0';}string(const string& s):_size(s._size), _capacity(s._size){_str = new char[_capacity + 1];strcpy(_str, s._str);}string& operator=(const string& s){if (this != &s){int len = strlen(s._str) ;char * p = new char[len + 1];strcpy(p, s._str);delete[]_str;_str = p;_size = len;_capacity = len;}return *this;}~string(){if (_str){delete[]_str;_str = nullptr;_capacity = 0;_size = 0;}}//容量相關操作size_t size()const{return _size;}size_t capacity()const{return _capacity;}bool empty()const{return 0 == _size;}void resize(size_t newsize,char ch){size_t oldsize = _size;if (newsize > oldsize){//有效元素增多//多出的元素再空余空間能否放的下if (newsize > _capacity){reserve(newsize);}memset(_str + _size, ch, newsize-oldsize);}_size = newsize;_str[_size] = '\0';}void reserve(size_t newcapacity){size_t oldcapacity = _capacity;if (newcapacity > oldcapacity){//申請新空間char * temp = new char[newcapacity + 1];//拷貝元素strcpy(temp, _str);//釋放舊空間delete[]_str;//指向新空間_str = temp;_capacity = newcapacity;}}//元素訪問相關操作char& operator[](size_t index){assert(index < _size);return _str[index];}const char& operator[]( int index){assert(index < _size);return _str[index];}//元素修改操作void push_back(char ch){if (_size == _capacity)reserve(_capacity * 2);_str[_size++] = ch;_str[_size] = '\0';}string& operator+=(const char ch){push_back(ch);return *this;}string& operator+=(const string s);bool operator==(const string s);bool operator!=(const string s);bool operator>=(const string s);bool operator<=(const string s);bool operator>(const string s);bool operator<(const string s);friend ostream& operator<< (ostream& _cout, const bite::string& s){_cout << s.c_str();return _cout;}friend istream operator>>(istream _cin, string s);//迭代器iterator begin(){return _str;}iterator end(){return _str + _size;}//特殊操作size_t find(char ch, size_t pos = 0){for (size_t i = pos; i < _size; i++){if (ch == _str[i])return i;}return npos;}size_t rfind(char ch, size_t pos = npos){if (pos == npos)pos = _size - 1;for (int i = pos; i >= 0; i--){if (ch == _str[i])return i;}return npos;}string substr(size_t pos = 0, size_t n = npos){if (n == npos)n = _size;string temp(_str + pos, _str + n + pos);return temp;}const char* c_str()const{return _str;}private:size_t _capacity; //當前空間有多大size_t _size; //當前string里有多少個有效字符char *_str;static size_t npos;};size_t string::npos = -1; }

要使用范圍for進行打印,必須要給出begin()和end()

總結

以上是生活随笔為你收集整理的浅拷贝+引用计数--写时拷贝---模拟实现string容器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。