借助Allocator实现一个类似vector的类Str。
思路:内存池
类使用(需求场景)
Str str1; // 默认构造Str str2 = { "mother fucker" }; // 初始化列表构造Str str3 = str2; // 拷贝构造Str str4 = std::move( str3 ); // 移动构造Str str5( str4 );str1 = str5; // 拷贝赋值str3 = std::move( str5 ); // 移动赋值str3.push_back( "motherfucker" ); // 触发push_back (string&&)string shit = "asdfasdf";str3.push_back(shit); // 触发push_back(const string&)str3.pop_back();
类定义
class Str final // 阻止被继承{public: Str() = default; ~Str() { free(); }public: // 拷贝控制 Str( const Str & ); // 拷贝构造 Str &operator=( const Str & ); // 拷贝赋值 Str( Str && ) noexcept; // 移动构造,因为移动资源的过程一般不会出现异常,声明成noexcept可以简化编译工作。 Str &operator=( Str && ) noexcept; // 移动赋值 Str( initializer_list<string> ); // 初始化列表构造 Str &operator=( initializer_list<string> ); // 值列表赋值public: Str &push_back( const string & ); // 插入 Str &push_back( string && ); // 插入 Str &pop_back(); // 删除尾后 size_t size() const { return first_free - elements; } // 当前元素个数 size_t capacity() const { return cap - elements; } // 当前容量 bool empty() const { return first_free == elements; } // 是否为空 string* begin() const { return elements; } // 首元素地址 string* end() const { return first_free; } // 最后一个元素的下一个地址private: void free(); // 释放全部内存 void checkAlloc(); // 检查是否需要重新分配内存 void reallocate(); // 内存不足,重新分配 string* allocateCopy( const string*, const string* ); // 分配一块内存并刚好装下begin ~ end的数据。private: static allocator<string> _allocator; // 分配器,所有Str对象共享 string* elements; // 首元素地址 string* first_free; // 最后一个元素地址 string* cap; // 尾后元素地址};
类函数实现
allocator<string> Str::_allocator;Str::Str( const Str &str ) // 拷贝构造{ elements = allocateCopy( str.begin(), str.end() ); // 特殊情况:nullptr + 0 = nullptr cap = first_free = elements + str.size(); }Str &Str::operator=( const Str &str ) // 拷贝赋值{ // 拷贝赋值第一步:查重,拷贝构造是三步曲,就少了这一步。 if( this == &str ) { return *this; } // 拷贝赋值第二步:拷贝副本 auto p = allocateCopy( str.begin(), str.end() ); // 拷贝赋值第三步:清除自身 free(); // 拷贝赋值第四步:赋值为副本 elements = p; cap = first_free = elements + str.size(); return *this;}Str::Str( Str &&str ) noexcept // 移动构造{ elements = str.elements; first_free = str.first_free; cap = str.cap; // 置空str str.elements = str.first_free = str.cap = nullptr;}Str &Str::operator=( Str &&str ) noexcept // 移动赋值{ if( &str == this ) { return *this; } free(); // 清除自身内存 elements = str.elements; // 接管 first_free = str.first_free; cap = str.cap; // 置空 str.elements = str.first_free = str.cap = nullptr; return *this;}Str::Str( initializer_list<string> lists ){ auto p = allocateCopy( lists.begin(), lists.end() ); elements = p; cap = first_free = elements + lists.size();}Str &Str::operator=( initializer_list<string> lists ){ auto p = allocateCopy( lists.begin(), lists.end() ); free(); elements = p; cap = first_free = elements + lists.size(); return *this;}void Str::free(){ if( empty() ) { return; } // 执行析构(逆序),非常容易漏掉这一步,直接回收内存,谨记谨记。 for( auto p = cap; p != elements; ) { _allocator.destroy( --p ); } // 回收内存 _allocator.deallocate( elements, capacity() ); // 置空 elements = first_free = cap = nullptr;}string* Str::allocateCopy( const string* begin, const string* end ){ auto p = _allocator.allocate( end - begin ); // end-begin=0,则返回nullptr uninitialized_copy( begin, end, p ); return p;}void Str::checkAlloc( ){ if( size() == capacity() ) { reallocate(); } // 目前的策略就是慢了就空间翻倍。}void Str::reallocate() // 存储空间翻倍{ size_t newCapacity = max( 1, ( cap - elements ) << 1 ); // << 1,可能有溢出风险。 auto p = _allocator.allocate( newCapacity ); // 拷贝旧内存数据到新内存,方法一 auto new_first_free = uninitialized_copy( make_move_iterator( elements ), make_move_iterator( first_free ), p ); // 拷贝旧内存数据到新内存,方法二 // for( auto tmp = _start, tmpP = p; tmp != _first_free; ) // { _allocator.construct( tmpP++, std::move( *tmp++ ) ); } free(); elements = p; first_free = new_first_free; cap = p + newCapacity;}Str &Str::push_back( const string &str ){ checkAlloc(); _allocator.construct( first_free++, str ); return *this;}Str &Str::push_back( string &&str ){ checkAlloc(); _allocator.construct( first_free++, std::move( str ) ); return *this;}Str &Str::pop_back(){ if( !empty() ) { _allocator.destroy( --first_free ); } return *this;}