借助Allocator实现一个类似vector的类Str。

思路:内存池

image.png

类使用(需求场景)

  1. Str str1; // 默认构造
  2. Str str2 = { "mother fucker" }; // 初始化列表构造
  3. Str str3 = str2; // 拷贝构造
  4. Str str4 = std::move( str3 ); // 移动构造
  5. Str str5( str4 );
  6. str1 = str5; // 拷贝赋值
  7. str3 = std::move( str5 ); // 移动赋值
  8. str3.push_back( "motherfucker" ); // 触发push_back (string&&)
  9. string shit = "asdfasdf";
  10. str3.push_back(shit); // 触发push_back(const string&)
  11. str3.pop_back();

类定义

  1. class Str final // 阻止被继承
  2. {
  3. public:
  4. Str() = default;
  5. ~Str() { free(); }
  6. public:
  7. // 拷贝控制
  8. Str( const Str & ); // 拷贝构造
  9. Str &operator=( const Str & ); // 拷贝赋值
  10. Str( Str && ) noexcept; // 移动构造,因为移动资源的过程一般不会出现异常,声明成noexcept可以简化编译工作。
  11. Str &operator=( Str && ) noexcept; // 移动赋值
  12. Str( initializer_list<string> ); // 初始化列表构造
  13. Str &operator=( initializer_list<string> ); // 值列表赋值
  14. public:
  15. Str &push_back( const string & ); // 插入
  16. Str &push_back( string && ); // 插入
  17. Str &pop_back(); // 删除尾后
  18. size_t size() const { return first_free - elements; } // 当前元素个数
  19. size_t capacity() const { return cap - elements; } // 当前容量
  20. bool empty() const { return first_free == elements; } // 是否为空
  21. string* begin() const { return elements; } // 首元素地址
  22. string* end() const { return first_free; } // 最后一个元素的下一个地址
  23. private:
  24. void free(); // 释放全部内存
  25. void checkAlloc(); // 检查是否需要重新分配内存
  26. void reallocate(); // 内存不足,重新分配
  27. string* allocateCopy( const string*, const string* ); // 分配一块内存并刚好装下begin ~ end的数据。
  28. private:
  29. static allocator<string> _allocator; // 分配器,所有Str对象共享
  30. string* elements; // 首元素地址
  31. string* first_free; // 最后一个元素地址
  32. string* cap; // 尾后元素地址
  33. };

类函数实现

  1. allocator<string> Str::_allocator;
  2. Str::Str( const Str &str ) // 拷贝构造
  3. {
  4. elements = allocateCopy( str.begin(), str.end() );
  5. // 特殊情况:nullptr + 0 = nullptr
  6. cap = first_free = elements + str.size();
  7. }
  8. Str &Str::operator=( const Str &str ) // 拷贝赋值
  9. {
  10. // 拷贝赋值第一步:查重,拷贝构造是三步曲,就少了这一步。
  11. if( this == &str ) { return *this; }
  12. // 拷贝赋值第二步:拷贝副本
  13. auto p = allocateCopy( str.begin(), str.end() );
  14. // 拷贝赋值第三步:清除自身
  15. free();
  16. // 拷贝赋值第四步:赋值为副本
  17. elements = p;
  18. cap = first_free = elements + str.size();
  19. return *this;
  20. }
  21. Str::Str( Str &&str ) noexcept // 移动构造
  22. {
  23. elements = str.elements;
  24. first_free = str.first_free;
  25. cap = str.cap;
  26. // 置空str
  27. str.elements = str.first_free = str.cap = nullptr;
  28. }
  29. Str &Str::operator=( Str &&str ) noexcept // 移动赋值
  30. {
  31. if( &str == this ) { return *this; }
  32. free(); // 清除自身内存
  33. elements = str.elements; // 接管
  34. first_free = str.first_free;
  35. cap = str.cap;
  36. // 置空
  37. str.elements = str.first_free = str.cap = nullptr;
  38. return *this;
  39. }
  40. Str::Str( initializer_list<string> lists )
  41. {
  42. auto p = allocateCopy( lists.begin(), lists.end() );
  43. elements = p;
  44. cap = first_free = elements + lists.size();
  45. }
  46. Str &Str::operator=( initializer_list<string> lists )
  47. {
  48. auto p = allocateCopy( lists.begin(), lists.end() );
  49. free();
  50. elements = p;
  51. cap = first_free = elements + lists.size();
  52. return *this;
  53. }
  54. void Str::free()
  55. {
  56. if( empty() ) { return; }
  57. // 执行析构(逆序),非常容易漏掉这一步,直接回收内存,谨记谨记。
  58. for( auto p = cap; p != elements; ) { _allocator.destroy( --p ); }
  59. // 回收内存
  60. _allocator.deallocate( elements, capacity() );
  61. // 置空
  62. elements = first_free = cap = nullptr;
  63. }
  64. string* Str::allocateCopy( const string* begin, const string* end )
  65. {
  66. auto p = _allocator.allocate( end - begin ); // end-begin=0,则返回nullptr
  67. uninitialized_copy( begin, end, p );
  68. return p;
  69. }
  70. void Str::checkAlloc( )
  71. {
  72. if( size() == capacity() ) { reallocate(); } // 目前的策略就是慢了就空间翻倍。
  73. }
  74. void Str::reallocate() // 存储空间翻倍
  75. {
  76. size_t newCapacity = max( 1, ( cap - elements ) << 1 ); // << 1,可能有溢出风险。
  77. auto p = _allocator.allocate( newCapacity );
  78. // 拷贝旧内存数据到新内存,方法一
  79. auto new_first_free = uninitialized_copy( make_move_iterator( elements ), make_move_iterator( first_free ), p );
  80. // 拷贝旧内存数据到新内存,方法二
  81. // for( auto tmp = _start, tmpP = p; tmp != _first_free; )
  82. // { _allocator.construct( tmpP++, std::move( *tmp++ ) ); }
  83. free();
  84. elements = p;
  85. first_free = new_first_free;
  86. cap = p + newCapacity;
  87. }
  88. Str &Str::push_back( const string &str )
  89. {
  90. checkAlloc();
  91. _allocator.construct( first_free++, str );
  92. return *this;
  93. }
  94. Str &Str::push_back( string &&str )
  95. {
  96. checkAlloc();
  97. _allocator.construct( first_free++, std::move( str ) );
  98. return *this;
  99. }
  100. Str &Str::pop_back()
  101. {
  102. if( !empty() ) { _allocator.destroy( --first_free ); }
  103. return *this;
  104. }