亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++是怎么實現string的

發布時間:2023-04-19 11:43:00 來源:億速云 閱讀:139 作者:iii 欄目:開發技術

本篇內容主要講解“C++是怎么實現string的”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“C++是怎么實現string的”吧!

常見的string實現方式有兩種,一種是深拷貝的方式,一種是COW(copy on write)寫時拷貝方式,以前多數使用COW方式,但由于目前多線程使用越來越多,COW技術在多線程中會有額外的性能惡化,所以現在多數使用深拷貝的方式,但了解COW的技術實現還是很有必要的。這里會對這兩種方式都進行源碼分析,正文內容較少,更多內容都在源碼的注釋中。

string的內容主要在gcc源碼的三個文件中:<string>、<basic_string.h>、<basic_string.tcc>

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

  • size: 表示真實數據的大小,一般resize函數改變的就是這個值。

  • capacity:表示內部實際已經分配的內存大小,capacity一定大于等于size,當size超過這個容量時會觸發重新分配機制,一般reserve函數改變的就是這個值。

深拷貝下string的實現

<string>文件中有如下代碼:

// file: string
using string = basic_string<char>;

這里可以看到string其實真實的樣子是basic_string,這里可以看下basic_string真實的結構:

template <typename _CharT, typename _Traits, typename _Alloc>
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指向實際的數據
         */
        pointer _M_p;  // The actual data.
    };

    _Alloc_hider _M_dataplus;
    /**
     * 真實數據的長度,等價于前面介紹的STL中的size
     */
    size_type _M_string_length;

    enum { _S_local_capacity = 15 / sizeof(_CharT) };

    /**
     * 這里有個小技巧,用了union
     * 因為使用_M_local_buf時候不需要關注_M_allocated_capacity
     * 使用_M_allocated_capacity時就不需要關注_M_local_buf
     * 繼續向下看完您就會明白。
     */
    union {
        _CharT _M_local_buf[_S_local_capacity + 1];
        /**
         * 內部已經分配的內存的大小,等價于前面介紹的STL中的capacity
         */
        size_type _M_allocated_capacity;
    };
};

從這里可以看見整個basic_string的結構如圖:

C++是怎么實現string的

看下面代碼:

string str;

這段代碼會調用普通構造函數,對應的源碼實現如下:

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

而_M_local_data()的實現如下:

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

這里可以看見_M_dataplus表示實際存放數據的地方,當string是空的時候,其實就是指向_M_local_buf,且_M_string_length是0。

當由char*構造string時,構造函數如下:

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的實現,具體分析可以看下我代碼中添加的注釋:

/***
 * _M_construct有很多種不同的實現,不同的迭代器類型有不同的優化實現,
 * 這里我們只需要關注一種即可,整體思路是相同的。
 */
template <typename _CharT, typename _Traits, typename _Alloc>
template <typename _InIterator>
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);
    // 現在__capacity是15,注意這個值等會可能會改變

    while (__beg != __end && __len < __capacity) {
        _M_data()[__len++] = *__beg;
        ++__beg;
    }
    /** 現在_M_data()指向的是_M_local_buf
     * 上面最多會拷貝_S_local_capacity即15個字節,繼續往下看,
     * 當超過_S_local_capacity時會重新申請一塊堆內存,_M_data()會去指向這塊新內存
     */
    __try {
        while (__beg != __end) {
            if (__len == __capacity) {
                /**
                 * 就是在這里,當string內capacity不夠容納len個字符時,會使用_M_create去擴容
                 * 這里你可能會有疑惑,貌似每次while循環都會去重新使用_M_create來申請多一個字節的內存
                 * 但其實不是,_M_create的第一個參數的傳遞方式是引用傳遞,__capacity會在內部被修改,稍后會分析
                 */
                __capacity = __len + 1;
                pointer __another = _M_create(__capacity, __len);
                /**
                 * 把舊數據拷貝到新的內存區域,_M_data()指向的是舊數據,__another指向的是新申請的內存
                 */
                this->_S_copy(__another, _M_data(), __len);
                /**
                 * __M_dispose()
                 * 釋放_M_data()指向的舊數據內存,如果是_M_local_buf則不需要釋放,稍后分析
                 */
                _M_dispose();
                /**
                 * _M_data()
                 * 內部的指向內存的指針指向這塊新申請的內存__another,它的實現其實就是
                 * void _M_data(pointer __p) { _M_dataplus._M_p = __p; }
                 */
                _M_data(__another);
                /**
                 * _M_allocated_capacity設置為__capacity
                 * 實現為 void _M_capacity(size_type __capacity) { _M_allocated_capacity = __capacity; }
                 */
                _M_capacity(__capacity);
            }
            _M_data()[__len++] = *__beg;
            ++__beg;
        }
    }
    __catch(...) {
        /**
         * 異常發生時,避免內存泄漏,會釋放掉內部申請的內存
         */
        _M_dispose();
        __throw_exception_again;
    }
    /**
     * 最后設置string的長度為__len
     * 實現為void _M_length(size_type __length) { _M_string_length = __length; }
     */
    _M_set_length(__len);
}

再分析下內部的內存申請函數_M_create:

/**
 * @brief _M_create表示申請新內存
 * @param __capacity 想要申請的內存大小,注意這里參數傳遞方式是引用傳遞,內部會改變其值
 * @param __old_capacity 以前的內存大小
 */
template <typename _CharT, typename _Traits, typename _Alloc>
typename basic_string<_CharT, _Traits, _Alloc>::pointer basic_string<_CharT, _Traits, _Alloc>::_M_create(
    size_type& __capacity, size_type __old_capacity) {
    /**
     * max_size()表示標準庫容器規定的一次性可以分配到最大內存大小
     * 當想要申請的內存大小最大規定長度時,會拋出異常
     */
    if (__capacity > max_size()) std::__throw_length_error(__N("basic_string::_M_create"));

    /**
     * 這里就是常見的STL動態擴容機制,其實常見的就是申請為__old_capacity的2倍大小的內存,最大只能申請max_size()
     * 注釋只是說了常見的內存分配大小思想,不全是下面代碼的意思,具體可以直接看下面這幾行代碼哈
     */
    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();
    }

    /**
     * 使用內存分配子去分配__capacity+1大小的內存,+1是為了多存儲個\0
     */
    return _Alloc_traits::allocate(_M_get_allocator(), __capacity + 1);
}

再分析下內部的內存釋放函數_M_dispose函數:

/**
 * 如果當前指向的是本地內存那15個字節,則不需要釋放
 * 如果不是,則需要使用_M_destroy去釋放其指向的內存
 */
void _M_dispose() {
    if (!_M_is_local()) _M_destroy(_M_allocated_capacity);
}

/**
 * 判斷下當前內部指向的是不是本地內存
 * _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的拷貝構造函數:

/**
 * basic_string的拷貝構造函數
 * 其實就是每次都做一次深拷貝
 */
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的賦值構造函數:

/**
 * 賦值構造函數,調用了assign函數
 */
basic_string& operator=(const basic_string& __str) { return this->assign(__str); }

/**
 * 調用了_M_assign函數
 */
basic_string& assign(const basic_string& __str) {
    this->_M_assign(__str);
    return *this;
}

/**
 * 賦值的核心函數
 */
template <typename _CharT, typename _Traits, typename _Alloc>
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不夠用,需要進行重新分配
         */
        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指向的內存拷貝到當前對象指向的內存上
         */
        if (__rsize) this->_S_copy(_M_data(), __str._M_data(), __rsize);

        _M_set_length(__rsize);
    }
}

再分析下移動構造函數:

/**
 * 移動構造函數,其實就是把src指向的內存移動到了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);
}

移動賦值函數和移動構造函數類似,就不作過多分析啦。

COW方式下string的實現

先看下部分源代碼了解下COW的basic_string的結構:

template <typename _CharT, typename _Traits, typename _Alloc>
class basic_string {
   private:
    struct _Rep_base {
        /**
         * string實際數據的大小
         * 字符串真正存儲的是正常字符串數據加上一個\0,真正的長度其實是_M_length+1
         */
        size_type _M_length;
        /**
         * string當前已經分配了的內存大小
         * _M_capacity一定不小于_M_length,內存分配總是以_M_capacity+1為單位
         */
        size_type _M_capacity;
        /**
         * _M_refcount表示string的引用計數,取值可以分為三種:
         * -1:可能內存泄漏,有一個變量指向字符串,字符串可以被更改,不允許拷貝,當
         * _M_refcount為-1時,表示這個string對象不會再和其它string對象共享啦。
         * 0:有一個變量指向字符串,字符串可以被更改。
         * n>=1:有n+1個變量指向字符串,對該字符串操作時應該加鎖,字符串不可以被更改。
         */
        _Atomic_word _M_refcount;
    };

    /**
     * _Rep繼承自_Rep_base
     * 主要目的就是繼承_Rep_base的三個成員_M_length、_M_capacity、_M_refcount
     */
    struct _Rep : _Rep_base {
        // Types:
        typedef typename _Alloc::template rebind<char>::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<void*>(&_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指向存儲實際數據的對象地址
    };

   public:
    static const size_type npos = static_cast<size_type>(-1);  // 0xFFFFFFFF

   private:
    /**
     * _M_dataplus是basic_string內部唯一的一個成員變量,
     * 內部有個_M_p成員,指向存儲實際的數據的對象,是_Rep對象的指針
     */
    mutable _Alloc_hider _M_dataplus;
};

具體分析可以看代碼中注釋,可以分析出COW的string結構如圖:

C++是怎么實現string的

前面程序喵分析過深拷貝方式下string的局部內存為_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 _CharT, typename _Traits, typename _Alloc>
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)];

再分析下構造函數:

/**
 * 使_M_dataplus指向_S_construct函數返回的內存
 */
template <typename _CharT, typename _Traits, typename _Alloc>
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) {}

/**
 * 返回一段內存,這段內存可以是本地空字符的內存,也可以是內存分配單元分配的內存
 */
template <typename _CharT, typename _Traits, typename _Alloc>
template <typename _InIterator>
_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字節時,分配len字節
     * 否則,以128為單位,每次擴容2倍大小
     * 稍后相信分析
     */
    _Rep* __r = _Rep::_S_create(__len, size_type(0), __a);
    /**
     * 將__buf指向的內存拷貝到數據真實存放的地址,_M_refdata()指向數據真實存放的地址
     * _M_refdata()函數實現如下,可以通過上面畫的string結構圖分析:
     * _CharT* _M_refdata() throw() { return reinterpret_cast<_CharT*>(this + 1); }
     * this+1就是數據真正的地址,這里的1代表sizeof(_Rep)
     */
    _M_copy(__r->_M_refdata(), __buf, __len);
    __try {
        /**
         * 這里的擴容機制和上面介紹的相同,這里就不過多介紹
         */
        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;
    }
    /**
     * 設置string的長度,同時設置該string是可共享的,稍后分析
     */
    __r->_M_set_length_and_sharable(__len);
    return __r->_M_refdata();
}

再看下string內部_M_create是如何申請內存的

template <typename _CharT, typename _Traits, typename _Alloc>
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是頁的大小,每次內存分配的最小單位
     * __malloc_header_zize是malloc分配內存額外需要的空間,存儲內存實際的長度信息
     */
    const size_type __pagesize = 4096;
    const size_type __malloc_header_size = 4 * sizeof(void*);

    /**
     * 每次兩倍擴容
     */
    if (__capacity > __old_capacity && __capacity < 2 * __old_capacity) __capacity = 2 * __old_capacity;

    /**
     * 看了前面的結構圖您應該就能明白為什么是這么計算,這里的+1是存儲字符串的結束符
     */
    size_type __size = (__capacity + 1) * sizeof(_CharT) + sizeof(_Rep);

    /**
     * 因為內存是以頁為基本單位分配的,所以這里做了一些優化,保證分配內存的大小是內存頁的整數倍
     */
    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內存位置處調用_Rep構造函數
     */
    _Rep* __p = new (__place) _Rep;
    __p->_M_capacity = __capacity;
    /**
     * 設置其可共享,實現如下
     * void _M_set_sharable() _GLIBCXX_NOEXCEPT { this->_M_refcount = 0; }
     */
    __p->_M_set_sharable();
    return __p;
}

這里有關于malloc的知識點可以看我之前寫的文章,前面Rep有個_M_set_length_and_sharable方法,看下它的源碼:

/**
 * 如果當前內存指向地址是本地內存則什么都不做,否則
 * 設置長度為n
 * 設置其可共享,其實就是設置引用計數為0
 * 同時在最后添加一個結束符\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版本主要就是為了避免過多的拷貝,這里看下string的拷貝構造函數:

/**
 * 這里是string的構造函數,主要是調用_Rep的_M_grab函數
 */
basic_string(const basic_string& __str, const _Alloc& __a)
    : _M_dataplus(__str._M_rep()->_M_grab(__a, __str.get_allocator()), __a) {}

/**
 * 前面已經介紹過為什么+1,這里您應該就知道為什么-1啦
 */
_Rep* _M_rep() const _GLIBCXX_NOEXCEPT { return &((reinterpret_cast<_Rep*>(_M_data()))[-1]); }

/**
 * _M_grab函數決定是將引用計數+1還是拷貝一份
 * 如果_M_is_leaked()表示不可以共享,則需要拷貝一份
 */
_CharT* _M_grab(const _Alloc& __alloc1, const _Alloc& __alloc2) {
    return (!_M_is_leaked() && __alloc1 == __alloc2) ? _M_refcopy() : _M_clone(__alloc1);
}

/**
 * 如果引用計數小于0,則為true,前面有過約定
 */
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
}

/**
 * 引用拷貝,其實就是引用計數+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 <typename _CharT, typename _Traits, typename _Alloc>
_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的析構函數:

/**
 * string的析構函數,調用了_M_dispose函數
 */
~basic_string() _GLIBCXX_NOEXCEPT { _M_rep()->_M_dispose(this->get_allocator()); }

/**
 * 將引用計數-1,如果引用計數 <= 0,則釋放內存
 */
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 <typename _CharT, typename _Traits, typename _Alloc>
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<char*>(this), __size);
}

data()和c_str()的區別

我們以前學習工作過程中都知道str有data和c_str函數,看資料都說它們的區別是一個帶\0結束符,一個不帶。 這里看下源碼:

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

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

這里可以看見它倆沒有任何區別,因為\0結束符其實在最開始構造string對象的時候就已經添加啦。

to_string是怎么實現的

這里直接看代碼:

inline string to_string(int __val) {
    return __gnu_cxx::__to_xstring<string>(&std::vsnprintf, 4 * sizeof(int), "%d", __val);
}

inline string to_string(unsigned __val) {
    return __gnu_cxx::__to_xstring<string>(&std::vsnprintf, 4 * sizeof(unsigned), "%u", __val);
}

inline string to_string(long __val) {
    return __gnu_cxx::__to_xstring<string>(&std::vsnprintf, 4 * sizeof(long), "%ld", __val);
}

template <typename _String, typename _CharT = typename _String::value_type>
_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);
}

這里可以看出所有的數值類型轉string,都是通過vsnprintf來實現,具體vsnprintf是什么這里就不過多介紹啦,讀者可以自行查找下相關用法哈。

到此,相信大家對“C++是怎么實現string的”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

通江县| 大英县| 保定市| 宜兰市| 大安市| 三穗县| 龙江县| 高碑店市| 永仁县| 贞丰县| 沈丘县| 盐亭县| 丹东市| 张掖市| 伊春市| 琼海市| 汉沽区| 临汾市| 乐亭县| 赣榆县| 兖州市| 信阳市| 义马市| 顺平县| 北宁市| 曲松县| 灵武市| 来宾市| 西昌市| 舒城县| 山东| 岳西县| 晋州市| 新田县| 辉南县| 阳泉市| 咸丰县| 紫阳县| 同江市| 阳东县| 博客|