国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

[C/C++]詳解STL容器1--string的功能和模擬實現(xiàn)(深淺拷貝問題)

tianren124 / 3430人閱讀

摘要:本文介紹了類的常用接口的使用,并對其進(jìn)行了模擬實現(xiàn),對模擬實現(xiàn)中涉及到的深淺拷貝問題進(jìn)行了解析。在此之前,必須提到一個經(jīng)典問題。為了解決淺拷貝問題,所以中引入了深拷貝。但是實際使用中需要是第一個形參對象,才能正常使用。

本文介紹了string類的常用接口的使用,并對其進(jìn)行了模擬實現(xiàn),對模擬實現(xiàn)中涉及到的深淺拷貝問題進(jìn)行了解析。

目錄

一、string類

1. C語言中的字符串

2. C++中的string類

二、string類的常用接口的使用

1. string類對象的常見構(gòu)造

?2. string類對象的容量操作

3. string類對象的訪問及遍歷操作

4.string類對象的修改操作?

5. string類非成員函數(shù)?

6.使用實例?

三、模擬實現(xiàn)

1. sring類的深淺拷貝問題

2. 淺拷貝

3. 深拷貝

(1)傳統(tǒng)寫法的string類

(2)現(xiàn)代寫法的string類

3.?寫時拷貝?

4.模擬實現(xiàn)完整代碼


一、string類

1. C語言中的字符串

?在C語言中,字符串是以"/0"結(jié)尾的一些字符的集合,C標(biāo)準(zhǔn)庫還提供了str系列的庫函數(shù),但是這些庫函數(shù)與字符串不太符合OOP的思想,底層空間需要用戶自己管理,可能會造成越界訪問。

2. C++中的string類

C++ 大大增強(qiáng)了對字符串的支持,除了可以使用C風(fēng)格的字符串,還可以使用內(nèi)置的 string 類。string 類處理起字符串來會方便很多,完全可以代替C語言中的字符數(shù)組或字符串指針。

string是表示字符串的字符串類,該類的接口與常規(guī)容器的接口基本相同,再添加了一些專門用來操作string的常規(guī)操作。不能操作多字節(jié)或者變長字符的序列。在底層實際是:basic_string模板類的別名,typedef basic_stringstring;

二、string類的常用接口的使用

1. string類對象的常見構(gòu)造

(constructor)函數(shù)名功能說明
string()構(gòu)造空的string類對象,即空字符串
string(const char* s)用C-string來構(gòu)造string類對象
string(size_t n, char c)string類對象中包含n個字符c
string(const string&s)拷貝構(gòu)造函數(shù)
void Teststring(){    string s1;             // 構(gòu)造空的string類對象s1    string s2("abcdef");   // 用C格式字符串構(gòu)造string類對象s2    string s3(s2);         // 拷貝構(gòu)造s3}

?2. string類對象的容量操作

函數(shù)名功能說明
size返回字符串有效字符長度,一般用作返回容器大小的方法
length

返回字符串有效字符長度,一般用作返回一個序列的長度

capacity返回空間總大小
empty檢測字符串釋放為空串,是返回true,否則返回false
clear清空有效字符
reserve為字符串預(yù)留空間
resize將有效字符的個數(shù)該成n個,多出的空間用字符c填充

這里的size()與length()方法底層實現(xiàn)原理完全相同,引入size()的原因是為了與其他容器的接口保持一致。

clear()只是將string中有效字符清空,不改變底層空間大小。

resize(size_t n) 與 resize(size_t n, char c)都是將字符串中有效字符個數(shù)改變到n個,不同的是當(dāng)字符個數(shù)增多時:resize(n)用0來填充多出的元素空間,resize(size_t n, char c)用字符c來填充多出的元素空間。

reserve(size_t res_arg=0):為string預(yù)留空間,不改變有效元素個數(shù),當(dāng)reserve的參數(shù)小于string的底層空間總大小時,reserver不會改變?nèi)萘看笮 ?/p>

3. string類對象的訪問及遍歷操作

函數(shù)名功能說明
operator[]返回pos位置的字符,const string類對象調(diào)用
begin+ endbegin獲取一個字符的迭代器 + end獲取最后一個字符下一個位置的迭代器
rbegin + rendbegin獲取一個字符的迭代器 + end獲取最后一個字符下一個位置的迭代器
范圍forC++11支持更簡潔的范圍for的新遍歷方式

三種迭代

void Teststring(){    string s("hello world");    // 3種遍歷方式:    // 1. for+operator[]    for(size_t i = 0; i < s.size(); ++i)         cout<

4.string類對象的修改操作?

函數(shù)名功能說明
push_back在字符串后尾插字符
append在字符串后追加一個字符串
operator+=在字符串后追加字符串
c_str返回C格式字符串
find + npos從字符串pos位置開始往后找字符c,返回該字符在字符串中的位置
rfind從字符串pos位置開始往前找字符c,返回該字符在字符串中的位置
substr在str中從pos位置開始,截取n個字符,然后將其返回

在string尾部追加字符時,s.push_back(c) / s.append(1, c) / s += "c"三種的實現(xiàn)方式差不多,一般
情況下string類的+=操作用的比較多,+=操作不僅可以連接單個字符,還可以連接字符串。

對string操作時,如果能夠大概預(yù)估到放多少字符,可以先通過reserve把空間預(yù)留好。?

5. string類非成員函數(shù)?

函數(shù)名功能說明
operator+盡量少用,因為傳值返回,導(dǎo)致深拷貝效率低
operator>>輸入運算符重載
operator<<輸出運算符重載
getline獲取一行字符串
relational operators大小比較

6.使用實例?

int main(){	/*****************構(gòu)造**********************/	string s1;				//無參	string s2("zhtzhtzht");		//帶參	string s3(s2);			//拷貝構(gòu)造	string s4 = "zhtzhtzhtzht";	//substring ,給多了或者給string::npos 都是走到尾	string s5(s4, 3, 5);	//從3開始5個	cout << s5 << endl;	string s6("123456", 3);			//取前三個構(gòu)造	cout << s6 << endl;	/*************三種遍歷***************/	//1.下標(biāo)+【】	for (size_t i = 0; i < s2.size(); i++)	{		cout << s2[i] << " ";	}	cout <

三、模擬實現(xiàn)

上文對string類進(jìn)行了簡單的介紹,接下來模擬實現(xiàn)string類的主要函數(shù)。在此之前,必須提到一個經(jīng)典問題。

1. sring類的深淺拷貝問題

class string{public:    string(const char* str = "")    {        // 構(gòu)造string類對象時,如果傳遞nullptr指針,認(rèn)為程序非法        if(nullptr == str)        {            assert(false);            return;        }    _str = new char[strlen(str) + 1];    strcpy(_str, str);}~string(){    if(_str)    {        delete[] _str;        _str = nullptr;    }}private:    char* _str;};void Teststring(){    string s1("hello");    string s2(s1);}

上述代碼會崩潰,string類沒有顯式定義其拷貝構(gòu)造函數(shù)與賦值運算符重載,此時編譯器會合成默認(rèn)的,當(dāng)用s1構(gòu)造s2時,編譯器會調(diào)用默認(rèn)的拷貝構(gòu)造。最終導(dǎo)致的問題是,s1、s2共用同一塊內(nèi)存空間,在釋放時同一塊空間被釋放多次而引起程序崩潰,這種拷貝方式,稱為淺拷貝

2. 淺拷貝

?淺拷貝:也稱位拷貝,編譯器只是將對象中的值拷貝過來。如果對象中管理資源,最后就會導(dǎo)致多個對象共享同一份資源,當(dāng)一個對象銷毀時就會將該資源釋放掉,而此時另一些對象不知道該資源已經(jīng)被釋放,所以當(dāng)繼續(xù)對資源進(jìn)項操作時,就會發(fā)生發(fā)生了訪問違規(guī)。

為了解決淺拷貝問題,所以C++中引入了深拷貝

3. 深拷貝

如果一個類中涉及到資源的管理,其拷貝構(gòu)造函數(shù)、賦值運算符重載以及析構(gòu)函數(shù)必須要顯式給出。

顯式地定義拷貝構(gòu)造函數(shù),它除了會將原有對象的所有成員變量拷貝給新對象,還會為新對象再分配一塊內(nèi)存,并將原有對象所持有的內(nèi)存也拷貝過來。這樣做的結(jié)果是,原有對象和新對象所持有的動態(tài)內(nèi)存是相互獨立的,更改一個對象的數(shù)據(jù)不會影響另外一個對象。

(1)傳統(tǒng)寫法的string類

class string{public:    string(const char* str = "")    {        if(nullptr == str)        {            assert(false);            return;        }        _str = new char[strlen(str) + 1];        strcpy(_str, str);    }    string(const string& s)        : _str(new char[strlen(s._str)+1])    {        strcpy(_str, s._str);    }    string& operator=(const string& s)    {        if(this != &s)        {            char* pStr = new char[strlen(s._str) + 1];            strcpy(pStr, s._str);            delete[] _str;            _str = pStr;        }        return *this;    }    ~string()    {        if(_str)        {            delete[] _str;            _str = nullptr;        }    }private:    char* _str;};

(2)現(xiàn)代寫法的string類

class string{public:    string(const char* str = "")    {        if(nullptr == str)        str = "";        _str = new char[strlen(str) + 1];        strcpy(_str, str);    }    string(const string& s)        : _str(nullptr)    {        string strTmp(s._str);        swap(_str, strTmp._str);    }    string& operator=(string s)    {        swap(_str, s._str);        return *this;    }    ~string()    {        if(_str)        {            delete[] _str;            _str = nullptr;        }    }private:    char* _str;};

3.?寫時拷貝?

寫時拷貝是在淺拷貝的基礎(chǔ)之上增加了引用計數(shù)的方式來實現(xiàn)的。

引用計數(shù):用來記錄資源使用者的個數(shù)。在構(gòu)造時,將資源的計數(shù)給成1,每增加一個對象使用該資源,就給計數(shù)增加1,當(dāng)某個對象被銷毀時,先給該計數(shù)減1,然后再檢查是否需要釋放資源,如果計數(shù)為1,說明該對象時資源的最后一個使用者,將該資源釋放;否則就不能釋放,因為還有其他對象在使用該資源。

4.模擬實現(xiàn)完整代碼

下面給出模擬實現(xiàn)的完整代碼以及需要注意的點

#include#include#include#includeusing std::cout;using std::endl;namespace zht{  class string  {      public:    typedef char* iterator; //容器迭代器本質(zhì)上是指針,通過typedef給char*重定義關(guān)鍵字    typedef const char* const_iterator;//迭代器需要提供const型,const 迭代器與普通迭代器在編譯器處理時會進(jìn)行修飾,構(gòu)成了函數(shù)重載    friend std::ostream& operator<<(std::ostream& out, const string& s);  //為了方便內(nèi)部引用,所以要設(shè)置為友元    friend std::istream& operator>>(std::istream& in, string& s);    iterator begin()  // 開始    {      return _str;    }    const_iterator begin() const //需要提供const類型迭代器,權(quán)限只能縮小不能放大,所以在處理const類型的問題時需要使用const類型的迭代器    {      return _str;    }    iterator end()    //結(jié)束    {      return _str + _size;  //迭代器結(jié)束實在空間的最后一位的后一個    }    const_iterator end() const    {      return _str + _size;    }   // operator&         string(const char* str = "")      //構(gòu)造函數(shù),現(xiàn)代寫法,減少創(chuàng)建的臨時對象的個數(shù)      :_str(new char[strlen(str) + 1])    {      _size = strlen(str);      _capacity = _size;      strcpy(_str,str);    }    //void swap(string& s)    //{     // ::swap(_str,s._str);      //::swap(_size,s._size);     //::swap(_capacity,s._capacity);    //}        //開空間    void reserve(std::size_t n)    {      if(n > _capacity) //當(dāng)N大于最大容量時擴(kuò)容      {        char* tmp = new char[n + 1];   //創(chuàng)建N+1個空間,需要保存/0.        strncpy(tmp, _str, _size + 1); //將原空間中的數(shù)據(jù)拷貝到新的中        delete []_str;        _str = tmp;               //更新        _capacity = n;      }    }    //開空間 + 初始化,重置capacity    void resize(std::size_t n, char ch = "/0")    {      //三情況,1.小于當(dāng)前的字符串長度,2.大于字符串長度但是小于空間大小;3.大于空間大小            if(n < _size)  //1.直接在n處加/0      {          _size = n;          _str[n] = "/0";            }      else      {          if(n > _capacity)     //3.擴(kuò)容,然后與2.合并           {              reserve(n);           }          for(std::size_t i = _size; i < _capacity; i++)  //從當(dāng)前字符串向后覆蓋          {              _str[i] = ch;          }          _str[_capacity] = "/0";          _size = n;      }    }    void swap(string& s)		{			std::swap(_str, s._str);			std::swap(_size, s._size);			std::swap(_capacity, s._capacity);		}    string(const string& s) //拷貝構(gòu)造函數(shù),現(xiàn)代寫法,通過創(chuàng)建一個新對象,交換,達(dá)到拷貝構(gòu)造的目的      :_str(NULL)      ,_size(0)      ,_capacity(0)    {      string tmp(s._str);      swap(tmp);    }    //binstring& operator+= (char ch)    //{          //}    string& operator=(string s)   // = 運算符重載    {      swap(s);      return *this;        }        ~string()    {      delete [] _str;      _str = NULL;      _size = 0;      _capacity = 0;    }        void clear()    {      _size = 0;      _str[0] = "/0";        }        //可讀可寫    char& operator[](std::size_t i)    {        assert(i < _size);    ///0,所以閉區(qū)間        return _str[i];    }    //只讀    const char& operator[](std::size_t i) const    {        assert(i < _size);        return _str[i];    }     ///返回對象中的字符串,用const    const char* c_str() const    {      return _str;    }    //pos位置插入    string& insert(std::size_t pos, char ch)    {      assert(pos <= _size);   //可以尾插,所以可以等于      //先判斷是否需要擴(kuò)容      if(_size == _capacity)      {          reserve(_capacity == 0 ? 4 : _capacity * 2);      }      //將數(shù)據(jù)后移      char* end = _size + _str;  //從/0開始挪      while (end >= _str + pos)//pos位需要挪      {          *(end + 1) = *end;  //end向后挪也就是end-1          --end;              //再向前      }      *(_str + pos) = ch;      _size++;      return *this;    }    //插入字符串    string& insert(std::size_t pos,const char* str)    {      assert(pos <= _size);      std::size_t len = strlen(str);            if(_size + len >  _capacity)//可能會直接大于      {          reserve(_size + len);      }      char* end = _size + _str;      while(end >= pos + _str)      {          *(end + len) = *end;          --end;      }      strncpy(_str + pos, str, len);      _size += len;      return *this;    }    void push_back(char ch)           //尾插字符    {        insert(_size,ch);    }    void append(const char* str)      //尾插字符串    {        insert(_size, str);    }    string& operator+=(char ch)         //重載+=字符    {        push_back(ch);        return *this;    }    string& operator+=(const char* str) //重載+=字符串    {        append(str);        return *this;    }    string& erase(std::size_t pos,std::size_t len = -1)    {        assert(pos < _size);        //兩種情況:        //1.剩余長度小于需要刪除的        //2.剩余長度大于需要刪除的        std::size_t LeftLen = _size - pos;        if(LeftLen <=  len)   // 小于,全刪除        {            _str[pos] = "/0";            _size = pos;        }        else                  //大于,len位向前補(bǔ)。        {            strcpy(_str + pos, _str + pos + len);            _size -= len;        }        return *this;    }    std::size_t find (char ch, std::size_t pos = 0)    {        assert(pos < _size);        for(std::size_t i = pos; i < _size; ++i)        {            if(_str[i] == ch)            {                return i;            }        }        return -1;    }    std::size_t find (const char* str, std::size_t pos = 0)    {        assert(pos < _size);        const char* ret = strstr(_str + pos, str);    //函數(shù)返回在 haystack 中第一次出現(xiàn) needle 字符串的位置,如果未找到則返回 null。        if(ret)        {          return ret - _str;        }        else{          return -1;        }    }    std::size_t size() const    {        return _size;    }  private:    char* _str;                   //字符串指針    std::size_t _size;            //使用的空間大小    std::size_t _capacity;        //空間大小  };  inline bool operator<(const string& s1, const string& s2)  {      return strcmp(s1.c_str(), s2.c_str()) < 0;        //strcmp(str1,str2),若str1=str2,則返回零;若str1str2,則返回正數(shù)  }  inline bool operator==(const string& s1, const string& s2)  {      return strcmp(s1.c_str(), s2.c_str()) == 0;  }  inline bool operator<=(const string& s1, const string& s2)  {      return s1 < s2 || s1 == s2;  }  inline bool operator!=(const string& s1, const string& s2)  {      return !(s1 == s2);  }  inline bool operator>(const string& s1, const string& s2)  {      return !(s1 <= s2);  }  inline bool operator>=(const string& s1, const string& s2)  {      return !(s1 < s2);  }    std::ostream& operator<<(std::ostream& out, const string& s)     //因為cout的輸出流對象和隱含的this指針在搶占第一個參數(shù)的位置。this指針默認(rèn)是第一個參數(shù)也就是左操作數(shù)了。    //但是實際使用中cout需要是第一個形參對象,才能正常使用。    //友元函數(shù)可以訪問   {     for(auto ch : s)    //使用范圍for遍歷字符串      {         out << ch;       //輸出到輸出流      }      return out;   }  std::istream& operator>>(std::istream& in,string& s)  {    s.clear();    char ch;    ch = in.get();    while(ch != " " && ch != "/n")    {      s += ch;      ch = in.get();    }        return in;  }}  

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/120816.html

相關(guān)文章

  • [C/C++]詳解STL容器3--list功能模擬實現(xiàn)(迭代器失效問題

    摘要:本文介紹了的常用接口的使用,并對其進(jìn)行了模擬實現(xiàn),包括迭代器的實現(xiàn)。與為反向迭代器,對迭代器執(zhí)行操作,迭代器向前移動。 本文介紹了list的常用接口的使用,并對其進(jìn)行了模擬實現(xiàn),包括list迭代器的實現(xiàn)。 目錄 一、list的介紹 二、list的常用接口的使用 1. list的構(gòu)造 2. l...

    amc 評論0 收藏0
  • [C/C++ -STL]vector使用及迭代器失效問題詳解

    摘要:函數(shù)底層實際上是對指針的操作隸書向,范圍內(nèi)比較等于的第一個元素返回迭代器。指定位置元素的刪除操作使用查找所在位置的刪除位置的數(shù)據(jù),導(dǎo)致迭代器失效。因此刪除中任意位置上元素時,就認(rèn)為該位置迭代器失效了。 ...

    VishKozus 評論0 收藏0
  • [C/C++ -STL]vector底層實現(xiàn)機(jī)制刨析

    摘要:并且由于的連續(xù)性,且循環(huán)中有迭代器的自加,所以在刪除一個元素后,迭代器需要減。隸書方案二與方案一在迭代器的處理上是類似的,不過對元素的訪問采用了迭代器的方法。 一、...

    lowett 評論0 收藏0
  • STL詳解(十)—— set、map、multiset、multimap介紹及使用

    摘要:注意當(dāng)中的和屬于容器適配器,它們默認(rèn)使用的基礎(chǔ)容器分別是和。拷貝構(gòu)造類型容器的復(fù)制品方式三使用迭代器拷貝構(gòu)造某一段內(nèi)容。若待插入元素的鍵值在當(dāng)中已經(jīng)存在,則函數(shù)插入失敗,并返回當(dāng)中鍵值為的元素的迭代器和。返回該迭代器位置元素的值。 ...

    不知名網(wǎng)友 評論0 收藏0
  • [C/C++ -STL]list模擬實現(xiàn)及l(fā)ist迭代器底層刨析

    摘要:對類采用三個模板來實現(xiàn)迭代器。楷體類中和模板定義分別對應(yīng)迭代器中模板定義的楷體采用向上傳值的方式,傳入不同值來采用不同迭代器。首先是迭代器的,分為前置和后置。迭代器的和都是獲取迭代器所對應(yīng)的值。唯一的差別就是就是用到了迭代器。 ...

    不知名網(wǎng)友 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<