真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

C++種string的實(shí)現(xiàn)方式

這篇文章主要講解了“C++種string的實(shí)現(xiàn)方式”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“C++種string的實(shí)現(xiàn)方式”吧!

成都創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比羅平網(wǎng)站開(kāi)發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式羅平網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋羅平地區(qū)。費(fèi)用合理售后完善,10余年實(shí)體公司更值得信賴。

常見(jiàn)的string實(shí)現(xiàn)方式有兩種,一種是深拷貝的方式,一種是COW(copy on write)寫(xiě)時(shí)拷貝方式,以前多數(shù)使用COW方式,但由于目前多線程使用越來(lái)越多,COW技術(shù)在多線程中會(huì)有額外的性能惡化,所以現(xiàn)在多數(shù)使用深拷貝的方式,但了解COW的技術(shù)實(shí)現(xiàn)還是很有必要的。

這里會(huì)對(duì)這兩種方式都進(jìn)行源碼分析,正文內(nèi)容較少,更多內(nèi)容都在源碼的注釋中。

string的內(nèi)容主要在gcc源碼的三個(gè)文件中:、、

在分析前先介紹下string或者C++ stl中幾個(gè)基本的概念:

  •  size: 表示真實(shí)數(shù)據(jù)的大小,一般resize函數(shù)改變的就是這個(gè)值。

  •  capacity:表示內(nèi)部實(shí)際已經(jīng)分配的內(nèi)存大小,capacity一定大于等于size,當(dāng)size超過(guò)這個(gè)容量時(shí)會(huì)觸發(fā)重新分配機(jī)制,一般reserve函數(shù)改變的就是這個(gè)值。

深拷貝下string的實(shí)現(xiàn)

文件中有如下代碼:

// file: string  using string = basic_string;

這里可以看到string其實(shí)真實(shí)的樣子是basic_string,這里可以看下basic_string真實(shí)的結(jié)構(gòu):

template   class basic_string {     // Use empty-base optimization: http://www.cantrip.org/emptyopt.html     struct _Alloc_hider : allocator_type  // TODO check __is_final    {         _Alloc_hider(pointer __dat, const _Alloc& __a) : allocator_type(__a), _M_p(__dat) {}         _Alloc_hider(pointer __dat, _Alloc&& __a = _Alloc()) : allocator_type(std::move(__a)), _M_p(__dat) {}         /**          * _M_p指向?qū)嶋H的數(shù)據(jù)          */         pointer _M_p;  // The actual data.    };     _Alloc_hider _M_dataplus;     /**      * 真實(shí)數(shù)據(jù)的長(zhǎng)度,等價(jià)于前面介紹的STL中的size      */     size_type _M_string_length;     enum { _S_local_capacity = 15 / sizeof(_CharT) };     /**      * 這里有個(gè)小技巧,用了union      * 因?yàn)槭褂胈M_local_buf時(shí)候不需要關(guān)注_M_allocated_capacity      * 使用_M_allocated_capacity時(shí)就不需要關(guān)注_M_local_buf      * 繼續(xù)向下看完您就會(huì)明白。      */     union {         _CharT _M_local_buf[_S_local_capacity + 1];         /**          * 內(nèi)部已經(jīng)分配的內(nèi)存的大小,等價(jià)于前面介紹的STL中的capacity          */         size_type _M_allocated_capacity;    };  };

從這里可以看見(jiàn)整個(gè)basic_string的結(jié)構(gòu)如圖:

C++種string的實(shí)現(xiàn)方式

看下面代碼:

string str;

這段代碼會(huì)調(diào)用普通構(gòu)造函數(shù),對(duì)應(yīng)的源碼實(shí)現(xiàn)如下:

basic_string() : _M_dataplus(_M_local_data()) { _M_set_length(0); }

而_M_local_data()的實(shí)現(xiàn)如下:

const_pointer _M_local_data() const {     return std::pointer_traits::pointer_to(*_M_local_buf);  }

這里可以看見(jiàn)M_dataplus表示實(shí)際存放數(shù)據(jù)的地方,當(dāng)string是空的時(shí)候,其實(shí)就是指向M_local_buf,且_M_string_length是0。

當(dāng)由char*構(gòu)造string時(shí),構(gòu)造函數(shù)如下:

basic_string(const _CharT* __s, size_type __n, const _Alloc& __a = _Alloc()) : _M_dataplus(_M_local_data(), __a) {     _M_construct(__s, __s + __n);  }

首先讓M_dataplus指向local_buf,再看下M_construct的實(shí)現(xiàn),具體分析可以看下我代碼中添加的注釋:

/***  * _M_construct有很多種不同的實(shí)現(xiàn),不同的迭代器類型有不同的優(yōu)化實(shí)現(xiàn),  * 這里我們只需要關(guān)注一種即可,整體思路是相同的。  */  template   template   void basic_string<_CharT, _Traits, _Alloc>::_M_construct(_InIterator __beg, _InIterator __end,                                                          std::input_iterator_tag) {    size_type __len = 0;    size_type __capacity = size_type(_S_local_capacity);    // 現(xiàn)在__capacity是15,注意這個(gè)值等會(huì)可能會(huì)改變    while (__beg != __end && __len < __capacity) {        _M_data()[__len++] = *__beg;        ++__beg;    }    /** 現(xiàn)在_M_data()指向的是_M_local_buf      * 上面最多會(huì)拷貝_S_local_capacity即15個(gè)字節(jié),繼續(xù)往下看,      * 當(dāng)超過(guò)_S_local_capacity時(shí)會(huì)重新申請(qǐng)一塊堆內(nèi)存,_M_data()會(huì)去指向這塊新內(nèi)存      */    __try {        while (__beg != __end) {            if (__len == __capacity) {                /**                  * 就是在這里,當(dāng)string內(nèi)capacity不夠容納len個(gè)字符時(shí),會(huì)使用_M_create去擴(kuò)容                  * 這里你可能會(huì)有疑惑,貌似每次while循環(huán)都會(huì)去重新使用_M_create來(lái)申請(qǐng)多一個(gè)字節(jié)的內(nèi)存                  * 但其實(shí)不是,_M_create的第一個(gè)參數(shù)的傳遞方式是引用傳遞,__capacity會(huì)在內(nèi)部被修改,稍后會(huì)分析                  */                __capacity = __len + 1;                pointer __another = _M_create(__capacity, __len);                /**                  * 把舊數(shù)據(jù)拷貝到新的內(nèi)存區(qū)域,_M_data()指向的是舊數(shù)據(jù),__another指向的是新申請(qǐng)的內(nèi)存                  */                this->_S_copy(__another, _M_data(), __len);                /**                  * __M_dispose()                  * 釋放_(tái)M_data()指向的舊數(shù)據(jù)內(nèi)存,如果是_M_local_buf則不需要釋放,稍后分析                  */                _M_dispose();                /**                  * _M_data()                  * 內(nèi)部的指向內(nèi)存的指針指向這塊新申請(qǐng)的內(nèi)存__another,它的實(shí)現(xiàn)其實(shí)就是                  * void _M_data(pointer __p) { _M_dataplus._M_p = __p; }                  */                _M_data(__another);                /**                  * _M_allocated_capacity設(shè)置為_(kāi)_capacity                  * 實(shí)現(xiàn)為 void _M_capacity(size_type __capacity) { _M_allocated_capacity = __capacity; }                  */                _M_capacity(__capacity);            }            _M_data()[__len++] = *__beg;            ++__beg;        }    }    __catch(...) {        /**          * 異常發(fā)生時(shí),避免內(nèi)存泄漏,會(huì)釋放掉內(nèi)部申請(qǐng)的內(nèi)存          */        _M_dispose();        __throw_exception_again;    }    /**      * 最后設(shè)置string的長(zhǎng)度為_(kāi)_len      * 實(shí)現(xiàn)為void _M_length(size_type __length) { _M_string_length = __length; }      */    _M_set_length(__len);  }

再分析下內(nèi)部的內(nèi)存申請(qǐng)函數(shù)_M_create:

/**  * @brief _M_create表示申請(qǐng)新內(nèi)存  * @param __capacity 想要申請(qǐng)的內(nèi)存大小,注意這里參數(shù)傳遞方式是引用傳遞,內(nèi)部會(huì)改變其值  * @param __old_capacity 以前的內(nèi)存大小  */  template   typename basic_string<_CharT, _Traits, _Alloc>::pointer basic_string<_CharT, _Traits, _Alloc>::_M_create(    size_type& __capacity, size_type __old_capacity) {    /**      * max_size()表示標(biāo)準(zhǔn)庫(kù)容器規(guī)定的一次性可以分配到最大內(nèi)存大小      * 當(dāng)想要申請(qǐng)的內(nèi)存大小最大規(guī)定長(zhǎng)度時(shí),會(huì)拋出異常      */    if (__capacity > max_size()) std::__throw_length_error(__N("basic_string::_M_create"));    /**      * 這里就是常見(jiàn)的STL動(dòng)態(tài)擴(kuò)容機(jī)制,其實(shí)常見(jiàn)的就是申請(qǐng)為_(kāi)_old_capacity的2倍大小的內(nèi)存,最大只能申請(qǐng)max_size()      * 注釋只是說(shuō)了常見(jiàn)的內(nèi)存分配大小思想,不全是下面代碼的意思,具體可以直接看下面這幾行代碼哈      */    if (__capacity > __old_capacity && __capacity < 2 * __old_capacity) {        __capacity = 2 * __old_capacity;        // Never allocate a string bigger than max_size.        if (__capacity > max_size()) __capacity = max_size();    }    /**      * 使用內(nèi)存分配子去分配__capacity+1大小的內(nèi)存,+1是為了多存儲(chǔ)個(gè)\0      */    return _Alloc_traits::allocate(_M_get_allocator(), __capacity + 1);  }

再分析下內(nèi)部的內(nèi)存釋放函數(shù)_M_dispose函數(shù):

/**  * 如果當(dāng)前指向的是本地內(nèi)存那15個(gè)字節(jié),則不需要釋放  * 如果不是,則需要使用_M_destroy去釋放其指向的內(nèi)存  */  void _M_dispose() {    if (!_M_is_local()) _M_destroy(_M_allocated_capacity);  }  /**  * 判斷下當(dāng)前內(nèi)部指向的是不是本地內(nèi)存  * _M_local_data()即返回_M_local_buf的地址  */  bool _M_is_local() const { return _M_data() == _M_local_data(); }  void _M_destroy(size_type __size) throw() {    _Alloc_traits::deallocate(_M_get_allocator(), _M_data(), __size + 1);  }

再分析下basic_string的拷貝構(gòu)造函數(shù):

/**  * basic_string的拷貝構(gòu)造函數(shù)  * 其實(shí)就是每次都做一次深拷貝  */  basic_string(const basic_string& __str)    : _M_dataplus(_M_local_data(), _Alloc_traits::_S_select_on_copy(__str._M_get_allocator())) {    _M_construct(__str._M_data(), __str._M_data() + __str.length());  }

再分析下basic_string的賦值構(gòu)造函數(shù):

/**  * 賦值構(gòu)造函數(shù),調(diào)用了assign函數(shù)  */  basic_string& operator=(const basic_string& __str) { return this->assign(__str); }  /**  * 調(diào)用了_M_assign函數(shù)  */  basic_string& assign(const basic_string& __str) {    this->_M_assign(__str);    return *this;  }  /**  * 賦值的核心函數(shù)  */  template   void basic_string<_CharT, _Traits, _Alloc>::_M_assign(const basic_string& __str) {    if (this != &__str) {        const size_type __rsize = __str.length();        const size_type __capacity = capacity();        /**          * 如果capacity不夠用,需要進(jìn)行重新分配          */        if (__rsize > __capacity) {            size_type __new_capacity = __rsize;            pointer __tmp = _M_create(__new_capacity, __capacity);            _M_dispose();            _M_data(__tmp);            _M_capacity(__new_capacity);        }        /**          * 將__str指向的內(nèi)存拷貝到當(dāng)前對(duì)象指向的內(nèi)存上          */        if (__rsize) this->_S_copy(_M_data(), __str._M_data(), __rsize);        _M_set_length(__rsize);    }  }

再分析下移動(dòng)構(gòu)造函數(shù):

/**  * 移動(dòng)構(gòu)造函數(shù),其實(shí)就是把src指向的內(nèi)存移動(dòng)到了dst種  */  basic_string(basic_string&& __str) noexcept : _M_dataplus(_M_local_data(), std::move(__str._M_get_allocator())) {    if (__str._M_is_local()) {        traits_type::copy(_M_local_buf, __str._M_local_buf, _S_local_capacity + 1);    } else {        _M_data(__str._M_data());        _M_capacity(__str._M_allocated_capacity);    }    // Must use _M_length() here not _M_set_length() because    // basic_stringbuf relies on writing into unallocated capacity so    // we mess up the contents if we put a '\0' in the string.    _M_length(__str.length());    __str._M_data(__str._M_local_data());    __str._M_set_length(0);  }

移動(dòng)賦值函數(shù)和移動(dòng)構(gòu)造函數(shù)類似,就不作過(guò)多分析啦。

COW方式下string的實(shí)現(xiàn)

先看下部分源代碼了解下COW的basic_string的結(jié)構(gòu):

template   class basic_string {    private:    struct _Rep_base {        /**          * string實(shí)際數(shù)據(jù)的大小          * 字符串真正存儲(chǔ)的是正常字符串?dāng)?shù)據(jù)加上一個(gè)\0,真正的長(zhǎng)度其實(shí)是_M_length+1          */        size_type _M_length;        /**          * string當(dāng)前已經(jīng)分配了的內(nèi)存大小          * _M_capacity一定不小于_M_length,內(nèi)存分配總是以_M_capacity+1為單位          */        size_type _M_capacity;        /**          * _M_refcount表示string的引用計(jì)數(shù),取值可以分為三種:          * -1:可能內(nèi)存泄漏,有一個(gè)變量指向字符串,字符串可以被更改,不允許拷貝,當(dāng)          * _M_refcount為-1時(shí),表示這個(gè)string對(duì)象不會(huì)再和其它string對(duì)象共享啦。          * 0:有一個(gè)變量指向字符串,字符串可以被更改。          * n>=1:有n+1個(gè)變量指向字符串,對(duì)該字符串操作時(shí)應(yīng)該加鎖,字符串不可以被更改。          */        _Atomic_word _M_refcount;    };    /**      * _Rep繼承自_Rep_base      * 主要目的就是繼承_Rep_base的三個(gè)成員_M_length、_M_capacity、_M_refcount      */    struct _Rep : _Rep_base {        // Types:        typedef typename _Alloc::template rebind::other _Raw_bytes_alloc;        static const size_type _S_max_size;        static const _CharT _S_terminal; // \0        static size_type _S_empty_rep_storage[]; // 這里大小不是0,稍后分析        static _Rep& _S_empty_rep() _GLIBCXX_NOEXCEPT {            // NB: Mild hack to avoid strict-aliasing warnings. Note that            // _S_empty_rep_storage is never modified and the punning should            // be reasonably safe in this case.            void* __p = reinterpret_cast(&_S_empty_rep_storage);            return *reinterpret_cast<_Rep*>(__p);        }    };    // Use empty-base optimization: http://www.cantrip.org/emptyopt.html    struct _Alloc_hider : _Alloc {        _Alloc_hider(_CharT* __dat, const _Alloc& __a) _GLIBCXX_NOEXCEPT : _Alloc(__a), _M_p(__dat) {}        _CharT* _M_p; // The actual data,這里的_M_p指向存儲(chǔ)實(shí)際數(shù)據(jù)的對(duì)象地址    };    public:    static const size_type npos = static_cast(-1); // 0xFFFFFFFF    private:    /**      * _M_dataplus是basic_string內(nèi)部唯一的一個(gè)成員變量,      * 內(nèi)部有個(gè)_M_p成員,指向存儲(chǔ)實(shí)際的數(shù)據(jù)的對(duì)象,是_Rep對(duì)象的指針      */    mutable _Alloc_hider _M_dataplus;  };

具體分析可以看代碼中注釋,可以分析出COW的string結(jié)構(gòu)如圖:

C++種string的實(shí)現(xiàn)方式

前面程序喵分析過(guò)深拷貝方式下string的局部?jī)?nèi)存為M_local_buf,那COW下string的S_empty_rep_storage是什么樣子呢?直接看源代碼:

// Linker sets _S_empty_rep_storage to all 0s (one reference, empty string)  // at static init time (before static ctors are run).  template   typename basic_string<_CharT, _Traits, _Alloc>::size_type basic_string<_CharT, _Traits, _Alloc>::_Rep::    _S_empty_rep_storage[(sizeof(_Rep_base) + sizeof(_CharT) + sizeof(size_type) - 1) / sizeof(size_type)];

再分析下構(gòu)造函數(shù):

/**  * 使_M_dataplus指向_S_construct函數(shù)返回的內(nèi)存  */  template   basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT* __s, size_type __n, const _Alloc& __a)    : _M_dataplus(_S_construct(__s, __s + __n, __a), __a) {}  /**  * 返回一段內(nèi)存,這段內(nèi)存可以是本地空字符的內(nèi)存,也可以是內(nèi)存分配單元分配的內(nèi)存  */  template   template   _CharT* basic_string<_CharT, _Traits, _Alloc>::_S_construct(_InIterator __beg, _InIterator __end, const _Alloc& __a,                                                            input_iterator_tag) {  #if _GLIBCXX_FULLY_DYNAMIC_STRING == 0    if (__beg == __end && __a == _Alloc()) return _S_empty_rep()._M_refdata();  #endif    // Avoid reallocation for common case.    _CharT __buf[128];    size_type __len = 0;    while (__beg != __end && __len < sizeof(__buf) / sizeof(_CharT)) {        __buf[__len++] = *__beg;        ++__beg;    }    /**      * len < 128字節(jié)時(shí),分配len字節(jié)      * 否則,以128為單位,每次擴(kuò)容2倍大小      * 稍后相信分析      */    _Rep* __r = _Rep::_S_create(__len, size_type(0), __a);    /**      * 將__buf指向的內(nèi)存拷貝到數(shù)據(jù)真實(shí)存放的地址,_M_refdata()指向數(shù)據(jù)真實(shí)存放的地址      * _M_refdata()函數(shù)實(shí)現(xiàn)如下,可以通過(guò)上面畫(huà)的string結(jié)構(gòu)圖分析:      * _CharT* _M_refdata() throw() { return reinterpret_cast<_CharT*>(this + 1); }      * this+1就是數(shù)據(jù)真正的地址,這里的1代表sizeof(_Rep)      */    _M_copy(__r->_M_refdata(), __buf, __len);    __try {        /**          * 這里的擴(kuò)容機(jī)制和上面介紹的相同,這里就不過(guò)多介紹          */        while (__beg != __end) {            if (__len == __r->_M_capacity) {                // Allocate more space.                _Rep* __another = _Rep::_S_create(__len + 1, __len, __a);                _M_copy(__another->_M_refdata(), __r->_M_refdata(), __len);                __r->_M_destroy(__a);                __r = __another;            }            __r->_M_refdata()[__len++] = *__beg;            ++__beg;        }   }    __catch(...) {        __r->_M_destroy(__a);        __throw_exception_again;    }    /**      * 設(shè)置string的長(zhǎng)度,同時(shí)設(shè)置該string是可共享的,稍后分析      */    __r->_M_set_length_and_sharable(__len);    return __r->_M_refdata();  }

再看下string內(nèi)部_M_create是如何申請(qǐng)內(nèi)存的

template   typename basic_string<_CharT, _Traits, _Alloc>::_Rep* basic_string<_CharT, _Traits, _Alloc>::_Rep::_S_create(    size_type __capacity, size_type __old_capacity, const _Alloc& __alloc) {    if (__capacity > _S_max_size) __throw_length_error(__N("basic_string::_S_create"));    /**      * __pagesize是頁(yè)的大小,每次內(nèi)存分配的最小單位      * __malloc_header_zize是malloc分配內(nèi)存額外需要的空間,存儲(chǔ)內(nèi)存實(shí)際的長(zhǎng)度信息      */    const size_type __pagesize = 4096;    const size_type __malloc_header_size = 4 * sizeof(void*);    /**      * 每次兩倍擴(kuò)容      */    if (__capacity > __old_capacity && __capacity < 2 * __old_capacity) __capacity = 2 * __old_capacity;    /**      * 看了前面的結(jié)構(gòu)圖您應(yīng)該就能明白為什么是這么計(jì)算,這里的+1是存儲(chǔ)字符串的結(jié)束符      */    size_type __size = (__capacity + 1) * sizeof(_CharT) + sizeof(_Rep);    /**      * 因?yàn)閮?nèi)存是以頁(yè)為基本單位分配的,所以這里做了一些優(yōu)化,保證分配內(nèi)存的大小是內(nèi)存頁(yè)的整數(shù)倍      */    const size_type __adj_size = __size + __malloc_header_size;    if (__adj_size > __pagesize && __capacity > __old_capacity) {        const size_type __extra = __pagesize - __adj_size % __pagesize;        __capacity += __extra / sizeof(_CharT);        // Never allocate a string bigger than _S_max_size.        if (__capacity > _S_max_size) __capacity = _S_max_size;        __size = (__capacity + 1) * sizeof(_CharT) + sizeof(_Rep);    }    // NB: Might throw, but no worries about a leak, mate: _Rep()    // does not throw.    void* __place = _Raw_bytes_alloc(__alloc).allocate(__size);    /**      * 這里是placement new,表示在__place內(nèi)存位置處調(diào)用_Rep構(gòu)造函數(shù)      */    _Rep* __p = new (__place) _Rep;    __p->_M_capacity = __capacity;    /**      * 設(shè)置其可共享,實(shí)現(xiàn)如下      * void _M_set_sharable() _GLIBCXX_NOEXCEPT { this->_M_refcount = 0; }      */    __p->_M_set_sharable();    return __p;  }  這里有關(guān)于malloc的知識(shí)點(diǎn)可以看我之前寫(xiě)的文章:xxx前面Rep有個(gè)_M_set_length_and_sharable方法,看下它的源碼:  /**   * 如果當(dāng)前內(nèi)存指向地址是本地內(nèi)存則什么都不做,否則   * 設(shè)置長(zhǎng)度為n   * 設(shè)置其可共享,其實(shí)就是設(shè)置引用計(jì)數(shù)為0   * 同時(shí)在最后添加一個(gè)結(jié)束符\0   */  void _M_set_length_and_sharable(size_type __n) _GLIBCXX_NOEXCEPT {  #if _GLIBCXX_FULLY_DYNAMIC_STRING == 0      if (__builtin_expect(this != &_S_empty_rep(), false))  #endif      {         this->_M_set_sharable();  // One reference.          this->_M_length = __n;          traits_type::assign(this->_M_refdata()[__n], _S_terminal);     }  }  void _M_set_sharable() _GLIBCXX_NOEXCEPT { this->_M_refcount = 0; }

COW版本主要就是為了避免過(guò)多的拷貝,這里看下string的拷貝構(gòu)造函數(shù):

/**   * 這里是string的構(gòu)造函數(shù),主要是調(diào)用_Rep的_M_grab函數(shù)   */  basic_string(const basic_string& __str, const _Alloc& __a)      : _M_dataplus(__str._M_rep()->_M_grab(__a, __str.get_allocator()), __a) {}  /**   * 前面已經(jīng)介紹過(guò)為什么+1,這里您應(yīng)該就知道為什么-1啦   */  _Rep* _M_rep() const _GLIBCXX_NOEXCEPT { return &((reinterpret_cast<_Rep*>(_M_data()))[-1]); }  /**   * _M_grab函數(shù)決定是將引用計(jì)數(shù)+1還是拷貝一份   * 如果_M_is_leaked()表示不可以共享,則需要拷貝一份   */  _CharT* _M_grab(const _Alloc& __alloc1, const _Alloc& __alloc2) {      return (!_M_is_leaked() && __alloc1 == __alloc2) ? _M_refcopy() : _M_clone(__alloc1);  }  /**   * 如果引用計(jì)數(shù)小于0,則為true,前面有過(guò)約定   */  bool _M_is_leaked() const _GLIBCXX_NOEXCEPT {  #if defined(__GTHREADS)      // _M_refcount is mutated concurrently by _M_refcopy/_M_dispose,      // so we need to use an atomic load. However, _M_is_leaked      // predicate does not change concurrently (i.e. the string is either      // leaked or not), so a relaxed load is enough.      return __atomic_load_n(&this->_M_refcount, __ATOMIC_RELAXED) < 0;  #else      return this->_M_refcount < 0;  #endif  }  /**   * 引用拷貝,其實(shí)就是引用計(jì)數(shù)+1   */  _CharT* _M_refcopy() throw() {  #if _GLIBCXX_FULLY_DYNAMIC_STRING == 0      if (__builtin_expect(this != &_S_empty_rep(), false))  #endif          __gnu_cxx::__atomic_add_dispatch(&this->_M_refcount, 1);      return _M_refdata();  }  // XXX MT    /**   * 深拷貝   */  template   _CharT* basic_string<_CharT, _Traits, _Alloc>::_Rep::_M_clone(const _Alloc& __alloc, size_type __res) {      // Requested capacity of the clone.      const size_type __requested_cap = this->_M_length + __res;      _Rep* __r = _Rep::_S_create(__requested_cap, this->_M_capacity, __alloc);      if (this->_M_length) _M_copy(__r->_M_refdata(), _M_refdata(), this->_M_length);      __r->_M_set_length_and_sharable(this->_M_length);      return __r->_M_refdata();  }  再分析下string的析構(gòu)函數(shù):  /**   * string的析構(gòu)函數(shù),調(diào)用了_M_dispose函數(shù)   */  ~basic_string() _GLIBCXX_NOEXCEPT { _M_rep()->_M_dispose(this->get_allocator()); }  /**   * 將引用計(jì)數(shù)-1,如果引用計(jì)數(shù) <= 0,則釋放內(nèi)存   */  void _M_dispose(const _Alloc& __a) _GLIBCXX_NOEXCEPT {  #if _GLIBCXX_FULLY_DYNAMIC_STRING == 0      if (__builtin_expect(this != &_S_empty_rep(), false))  #endif      {          // Be race-detector-friendly.  For more info see bits/c++config.          _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&this->_M_refcount);          if (__gnu_cxx::__exchange_and_add_dispatch(&this->_M_refcount, -1) <= 0) {              _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&this->_M_refcount);              _M_destroy(__a);          }      }  }  // XXX MT  template   void basic_string<_CharT, _Traits, _Alloc>::_Rep::_M_destroy(const _Alloc& __a) throw() {      const size_type __size = sizeof(_Rep_base) + (this->_M_capacity + 1) * sizeof(_CharT);      _Raw_bytes_alloc(__a).deallocate(reinterpret_cast(this), __size);  }

data()和c_str()的區(qū)別

我們以前學(xué)習(xí)工作過(guò)程中都知道str有data和c_str函數(shù),看資料都說(shuō)它們的區(qū)別是一個(gè)帶\0結(jié)束符,一個(gè)不帶。這里看下源碼:

const _CharT* c_str() const _GLIBCXX_NOEXCEPT { return _M_data(); }  const _CharT* data() const _GLIBCXX_NOEXCEPT { return _M_data(); }

這里可以看見(jiàn)它倆沒(méi)有任何區(qū)別,因?yàn)閈0結(jié)束符其實(shí)在最開(kāi)始構(gòu)造string對(duì)象的時(shí)候就已經(jīng)添加啦。

to_string是怎么實(shí)現(xiàn)的?

這里直接看代碼:

inline string to_string(int __val) {      return __gnu_cxx::__to_xstring(&std::vsnprintf, 4 * sizeof(int), "%d", __val);  }  inline string to_string(unsigned __val) {      return __gnu_cxx::__to_xstring(&std::vsnprintf, 4 * sizeof(unsigned), "%u", __val);  }  inline string to_string(long __val) {      return __gnu_cxx::__to_xstring(&std::vsnprintf, 4 * sizeof(long), "%ld", __val);  }  template   _String __to_xstring(int (*__convf)(_CharT*, std::size_t, const _CharT*, __builtin_va_list), std::size_t __n,                       const _CharT* __fmt, ...) {      // XXX Eventually the result should be constructed in-place in      // the __cxx11 string, likely with the help of internal hooks.      _CharT* __s = static_cast<_CharT*>(__builtin_alloca(sizeof(_CharT) * __n));      __builtin_va_list __args;      __builtin_va_start(__args, __fmt);      const int __len = __convf(__s, __n, __fmt, __args);      __builtin_va_end(__args);      return _String(__s, __s + __len);  }

這里可以看出所有的數(shù)值類型轉(zhuǎn)string,都是通過(guò)vsnprintf來(lái)實(shí)現(xiàn),具體vsnprintf是什么這里就不過(guò)多介紹啦,讀者可以自行查找下相關(guān)用法。

感謝各位的閱讀,以上就是“C++種string的實(shí)現(xiàn)方式”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)C++種string的實(shí)現(xiàn)方式這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!


標(biāo)題名稱:C++種string的實(shí)現(xiàn)方式
標(biāo)題路徑:http://weahome.cn/article/geishp.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部