std::string
1
| typedef basic_string<char, char_traits<char>, allocator<char> > string;
|
数据结构
在basic_string实现中,对短字符串进行了优化,长短字符长存储的数据结构是不一样的。
1 2 3 4 5 6 7 8 9 10 11
| struct __rep { union { __long __l; __short __s; __raw __r; }; };
__compressed_pair<__rep, allocator_type> __r_;
|
长字符串
长字符串会根据字符大小动态分配内存,data中存储字符数组的指针,当字符大小超过分配的长度时,会进行扩容。
1 2 3 4 5 6 7
| struct __long { size_type __cap_; size_type __size_; pointer __data_; };
|
短字符串
短字符串最多存储22个字符(最后一位为0),所以小于23长度的都是短字符串存储,超过都是长字符串存储。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| enum {__min_cap = (sizeof(__long) - 1)/sizeof(value_type) > 2 ? (sizeof(__long) - 1)/sizeof(value_type) : 2};
struct __short { union { unsigned char __size_; value_type __lx; }; value_type __data_[__min_cap]; };
|
长短字符串标记
通过在数据结构的第一个字节插入标记来区分当前是长字符还是短字符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #ifdef _LIBCPP_BIG_ENDIAN static const size_type __short_mask = 0x80; static const size_type __long_mask = ~(size_type(~0) >> 1); #else static const size_type __short_mask = 0x01; static const size_type __long_mask = 0x1ul; #endif
在小端环境下,对于短字符,会把size字段的最低位标记为0(通过左移)。大端环境下,由于短字符的size最大为22,所以最高位一定是0. ```c++ _LIBCPP_INLINE_VISIBILITY void __set_short_size(size_type __s) _NOEXCEPT # ifdef _LIBCPP_BIG_ENDIAN {__r_.first().__s.__size_ = (unsigned char)(__s);} # else {__r_.first().__s.__size_ = (unsigned char)(__s << 1);} # endif
|
对于长字符,其分配的内存大小都是经过字节对齐的,所以其最低位一定是0,通过把cap字段的最低位标记为1(大端环境下是把最高位标记为1),来标记是长字符。
1 2 3
| _LIBCPP_INLINE_VISIBILITY void __set_long_cap(size_type __s) _NOEXCEPT {__r_.first().__l.__cap_ = __long_mask | __s;}
|
所以判断是否是长字符,只需要判断其第一个字节的最低位是否是1即可。
1 2 3
| _LIBCPP_INLINE_VISIBILITY bool __is_long() const _NOEXCEPT {return bool(__r_.first().__s.__size_ & __short_mask);}
|
构造函数
basic_string提供了多种构造,可以方便的创建。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| basic_string() noexcept; explicit basic_string(const allocator_type& a); basic_string(const basic_string& str); basic_string(basic_string&& str) noexcept; basic_string(const basic_string& str, size_type pos, const allocator_type& a = allocator_type()); basic_string(const basic_string& str, size_type pos, size_type n, const Allocator& a = Allocator()); template<class T> basic_string(const T& t, size_type pos, size_type n, const Allocator& a = Allocator()); template <class T> explicit basic_string(const T& t, const Allocator& a = Allocator()); basic_string(const value_type* s, const allocator_type& a = allocator_type()); basic_string(const value_type* s, size_type n, const allocator_type& a = allocator_type()); basic_string(size_type n, value_type c, const allocator_type& a = allocator_type()); template<class InputIterator> basic_string(InputIterator begin, InputIterator end, const allocator_type& a = allocator_type()); basic_string(initializer_list<value_type>, const Allocator& = Allocator()); basic_string(const basic_string&, const Allocator&); basic_string(basic_string&&, const Allocator&);
|
init
一个string构造初始化流程基本如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| template <class _CharT, class _Traits, class _Allocator> void basic_string<_CharT, _Traits, _Allocator>::__init(const value_type* __s, size_type __sz) { if (__sz > max_size()) this->__throw_length_error(); pointer __p; if (__sz < __min_cap) { __set_short_size(__sz); __p = __get_short_pointer(); } else { size_type __cap = __recommend(__sz); __p = __alloc_traits::allocate(__alloc(), __cap+1); __set_long_pointer(__p); __set_long_cap(__cap+1); __set_long_size(__sz); } traits_type::copy(_VSTD::__to_address(__p), __s, __sz); traits_type::assign(__p[__sz], value_type()); }
|
append
通过+=操作符进行字符串加操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| template <class _CharT, class _Traits, class _Allocator> basic_string<_CharT, _Traits, _Allocator>& basic_string<_CharT, _Traits, _Allocator>::append(const value_type* __s, size_type __n) { _LIBCPP_ASSERT(__n == 0 || __s != nullptr, "string::append received nullptr"); size_type __cap = capacity(); size_type __sz = size(); if (__cap - __sz >= __n) { if (__n) { value_type* __p = _VSTD::__to_address(__get_pointer()); traits_type::copy(__p + __sz, __s, __n); __sz += __n; __set_size(__sz); traits_type::assign(__p[__sz], value_type()); } } else __grow_by_and_replace(__cap, __sz + __n - __cap, __sz, __sz, 0, __n, __s); return *this; }
|
数据扩容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| template <class _CharT, class _Traits, class _Allocator> void basic_string<_CharT, _Traits, _Allocator>::__grow_by_and_replace (size_type __old_cap, size_type __delta_cap, size_type __old_sz, size_type __n_copy, size_type __n_del, size_type __n_add, const value_type* __p_new_stuff) { size_type __ms = max_size(); if (__delta_cap > __ms - __old_cap - 1) this->__throw_length_error(); pointer __old_p = __get_pointer(); size_type __cap = __old_cap < __ms / 2 - __alignment ? __recommend(_VSTD::max(__old_cap + __delta_cap, 2 * __old_cap)) : __ms - 1; pointer __p = __alloc_traits::allocate(__alloc(), __cap+1); __invalidate_all_iterators(); if (__n_copy != 0) traits_type::copy(_VSTD::__to_address(__p), _VSTD::__to_address(__old_p), __n_copy); if (__n_add != 0) traits_type::copy(_VSTD::__to_address(__p) + __n_copy, __p_new_stuff, __n_add); size_type __sec_cp_sz = __old_sz - __n_del - __n_copy; if (__sec_cp_sz != 0) traits_type::copy(_VSTD::__to_address(__p) + __n_copy + __n_add, _VSTD::__to_address(__old_p) + __n_copy + __n_del, __sec_cp_sz); if (__old_cap+1 != __min_cap) __alloc_traits::deallocate(__alloc(), __old_p, __old_cap+1); __set_long_pointer(__p); __set_long_cap(__cap+1); __old_sz = __n_copy + __n_add + __sec_cp_sz; __set_long_size(__old_sz); traits_type::assign(__p[__old_sz], value_type()); }
|
resize
设置字符长度,设置的是size长度。
1 2 3
| _LIBCPP_INLINE_VISIBILITY void resize(size_type __n) { resize(__n, value_type()); }
|
1 2 3 4 5 6 7 8 9 10
| template <class _CharT, class _Traits, class _Allocator> void basic_string<_CharT, _Traits, _Allocator>::resize(size_type __n, value_type __c) { size_type __sz = size(); if (__n > __sz) append(__n - __sz, __c); else __erase_to_end(__n); }
|
reserve
设置分配内存大小,设置的是cap大小,size不会变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| template <class _CharT, class _Traits, class _Allocator> void basic_string<_CharT, _Traits, _Allocator>::reserve(size_type __requested_capacity) { if (__requested_capacity > max_size()) this->__throw_length_error();
#if _LIBCPP_STD_VER > 17 if (__requested_capacity <= capacity()) return; #endif
size_type __target_capacity = _VSTD::max(__requested_capacity, size()); __target_capacity = __recommend(__target_capacity); if (__target_capacity == capacity()) return;
__shrink_or_extend(__target_capacity); }
|
clear
清空字符长度,并不会销毁内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| template <class _CharT, class _Traits, class _Allocator> inline void basic_string<_CharT, _Traits, _Allocator>::clear() _NOEXCEPT { __invalidate_all_iterators(); if (__is_long()) { traits_type::assign(*__get_long_pointer(), value_type()); __set_long_size(0); } else { traits_type::assign(*__get_short_pointer(), value_type()); __set_short_size(0); } }
|
operator[]
1 2 3 4 5 6 7 8
| template <class _CharT, class _Traits, class _Allocator> inline typename basic_string<_CharT, _Traits, _Allocator>::reference basic_string<_CharT, _Traits, _Allocator>::operator[](size_type __pos) _NOEXCEPT { _LIBCPP_ASSERT(__pos <= size(), "string index out of bounds"); return *(__get_pointer() + __pos); }
|
at
at多了一个越界的检查,会抛出异常,里面也是调用[]操作符
1 2 3 4 5 6 7 8
| template <class _CharT, class _Traits, class _Allocator> typename basic_string<_CharT, _Traits, _Allocator>::reference basic_string<_CharT, _Traits, _Allocator>::at(size_type __n) { if (__n >= size()) this->__throw_out_of_range(); return (*this)[__n]; }
|
insert
在指定位置插入新字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| template <class _CharT, class _Traits, class _Allocator> basic_string<_CharT, _Traits, _Allocator>& basic_string<_CharT, _Traits, _Allocator>::insert(size_type __pos, const value_type* __s, size_type __n) { _LIBCPP_ASSERT(__n == 0 || __s != nullptr, "string::insert received nullptr"); size_type __sz = size(); if (__pos > __sz) this->__throw_out_of_range(); size_type __cap = capacity(); if (__cap - __sz >= __n) { if (__n) { value_type* __p = _VSTD::__to_address(__get_pointer()); size_type __n_move = __sz - __pos; if (__n_move != 0) { if (__p + __pos <= __s && __s < __p + __sz) __s += __n; traits_type::move(__p + __pos + __n, __p + __pos, __n_move); } traits_type::move(__p + __pos, __s, __n); __sz += __n; __set_size(__sz); traits_type::assign(__p[__sz], value_type()); } } else __grow_by_and_replace(__cap, __sz + __n - __cap, __sz, __pos, 0, __n, __s); return *this; }
|
erase
删除指定位置后面的指定长度的数据,只是改变了size,并没有销毁内存
1 2 3 4 5 6 7 8 9 10 11 12
| template <class _CharT, class _Traits, class _Allocator> basic_string<_CharT, _Traits, _Allocator>& basic_string<_CharT, _Traits, _Allocator>::erase(size_type __pos, size_type __n) { if (__pos > size()) this->__throw_out_of_range(); if (__n == npos) { __erase_to_end(__pos); } else { __erase_external_with_move(__pos, __n); } return *this; }
|
replace
pos到pos+n1位置的数据用s到s+n2的数据来替换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| template <class _CharT, class _Traits, class _Allocator> basic_string<_CharT, _Traits, _Allocator>& basic_string<_CharT, _Traits, _Allocator>::replace(size_type __pos, size_type __n1, const value_type* __s, size_type __n2) _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK { _LIBCPP_ASSERT(__n2 == 0 || __s != nullptr, "string::replace received nullptr"); size_type __sz = size(); if (__pos > __sz) this->__throw_out_of_range(); __n1 = _VSTD::min(__n1, __sz - __pos); size_type __cap = capacity(); if (__cap - __sz + __n1 >= __n2) { value_type* __p = _VSTD::__to_address(__get_pointer()); if (__n1 != __n2) { size_type __n_move = __sz - __pos - __n1; if (__n_move != 0) { if (__n1 > __n2) { traits_type::move(__p + __pos, __s, __n2); traits_type::move(__p + __pos + __n2, __p + __pos + __n1, __n_move); goto __finish; } if (__p + __pos < __s && __s < __p + __sz) { if (__p + __pos + __n1 <= __s) __s += __n2 - __n1; else { traits_type::move(__p + __pos, __s, __n1); __pos += __n1; __s += __n2; __n2 -= __n1; __n1 = 0; } } traits_type::move(__p + __pos + __n2, __p + __pos + __n1, __n_move); } } traits_type::move(__p + __pos, __s, __n2); __finish:
__sz += __n2 - __n1; __set_size(__sz); __invalidate_iterators_past(__sz); traits_type::assign(__p[__sz], value_type()); } else __grow_by_and_replace(__cap, __sz - __n1 + __n2 - __cap, __sz, __pos, __n1, __n2, __s); return *this; }
|
substr
会重新拷贝一份数据
1 2 3 4 5 6 7
| template <class _CharT, class _Traits, class _Allocator> inline basic_string<_CharT, _Traits, _Allocator> basic_string<_CharT, _Traits, _Allocator>::substr(size_type __pos, size_type __n) const { return basic_string(*this, __pos, __n, __alloc()); }
|
operator std::string_view
支持隐式转换为std::string_view
1 2 3 4
| _LIBCPP_INLINE_VISIBILITY operator __self_view() const _NOEXCEPT { return __self_view(data(), size()); }
|
std::string_view
string_view是一个轻量级的字符串类,相比string每次都会拷贝数据,在string_view中只会存储数据的指针。string_view相比较char*提供了很多字符操作,也就是说如果想要使用string里面的字符操作函数,但是又不想到拷贝一份数据,可以使用string_view。
1
| typedef basic_string_view<char> string_view;
|
数据结构
1 2 3
| private: const value_type* __data; size_type __size;
|
构造函数
string_view的构造只是简单的记录了外部传入的字符数组的指针和长度,并不会和string一样自身拷贝一份。string对象可以隐式转换为string_view,所以可以直接将string对象作为参数进行构造。
1 2 3 4 5 6
| constexpr basic_string_view() noexcept; constexpr basic_string_view(const basic_string_view&) noexcept = default; basic_string_view& operator=(const basic_string_view&) noexcept = default; template<class Allocator> constexpr basic_string_view(const charT* str); constexpr basic_string_view(const charT* str, size_type len);
|
1 2 3 4 5 6 7
| _LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY basic_string_view(const _CharT* __s, size_type __len) _NOEXCEPT : __data(__s), __size(__len) {}
_LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY basic_string_view(const _CharT* __s) : __data(__s), __size(_VSTD::__char_traits_length_checked<_Traits>(__s)) {}
|
substr
string_view的substr并不会重新拷贝一份
1 2 3 4 5 6 7
| _LIBCPP_CONSTEXPR _LIBCPP_INLINE_VISIBILITY basic_string_view substr(size_type __pos = 0, size_type __n = npos) const { return __pos > size() ? (__throw_out_of_range("string_view::substr"), basic_string_view()) : basic_string_view(data() + __pos, _VSTD::min(__n, size() - __pos)); }
|