本文參考 https://zhuanlan.zhihu.com/p/267896855
// file: string
using string = basic_string<char>;
1 深拷貝下 string 實(shí)現(xiàn)
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指向?qū)嶋H的數(shù)據(jù)
pointer _M_p; // The actual data.
};
// (1)
_Alloc_hider _M_dataplus;
// (2) 數(shù)據(jù)長(zhǎng)度 <=> string::size()
size_type _M_string_length;
// 容量用枚舉
enum { _S_local_capacity = 15 / sizeof(_CharT) }; // 通常 = 15
/**
* (3) 小技巧惜纸, 用了union: 用 _M_local_buf 與 _M_allocated_capacity 用一個(gè)時(shí) 不需要關(guān)注另一個(gè)
*/
union {
_CharT _M_local_buf[_S_local_capacity + 1]; // 16
// 內(nèi)部已分配的內(nèi)存大小, <=> string::capacity()
size_type _M_allocated_capacity;
};
};
Ctor
basic_string() : _M_dataplus(_M_local_data() )
{ _M_set_length(0); }
const_pointer
_M_local_data() const
{
return std::pointer_traits<const_pointer>::pointer_to(*_M_local_buf);
}
void
_M_set_length(size_type __n)
{
_M_length(__n);
/* _charT(): 即調(diào)用 char 類型的默認(rèn)構(gòu)造函數(shù), 得 '\0', 為末尾添結(jié)束符 '\0' */
traits_type::assign(_M_data()[__n], _CharT() );
}
Copy Ctor: 每次都做1次 深拷貝
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() );
}
data() 和 c_str() 區(qū)別: 沒區(qū)別
Note: '\0' 結(jié)束符在構(gòu)造 string 對(duì)象時(shí)已經(jīng)添加了
const _CharT*
c_str() const _GLIBCXX_NOEXCEPT
{ return _M_data(); }
const _CharT*
data() const _GLIBCXX_NOEXCEPT
{ return _M_data(); }
pointer
_M_data() const
{ return _M_dataplus._M_p; }
2 string 寫時(shí)拷貝 (COW) 實(shí)現(xiàn): 主要是為了避免過多的拷貝
Copy ctor
/* Ctor: 調(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)介紹過為什么+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, 前面有過約定 */
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 <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();
}