借助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;
}