string 实现
2022-10-09 本文已影响0人
my_passion
本文参考 https://zhuanlan.zhihu.com/p/267896855
// file: string
using string = basic_string<char>;
1 深拷贝下 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.
};
// (1)
_Alloc_hider _M_dataplus;
// (2) 数据长度 <=> string::size()
size_type _M_string_length;
// 容量用枚举
enum { _S_local_capacity = 15 / sizeof(_CharT) }; // 通常 = 15
/**
* (3) 小技巧, 用了union: 用 _M_local_buf 与 _M_allocated_capacity 用一个时 不需要关注另一个
*/
union {
_CharT _M_local_buf[_S_local_capacity + 1]; // 16
// 内部已分配的内存大小, <=> string::capacity()
size_type _M_allocated_capacity;
};
};
string 成员数据.png
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(): 即调用 char 类型的默认构造函数, 得 '\0', 为末尾添结束符 '\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() 区别: 没区别
Note: '\0' 结束符在构造 string 对象时已经添加了
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 写时拷贝 (COW) 实现: 主要是为了避免过多的拷贝
成员数据.pngCopy ctor
/* Ctor: 调 _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();
}