编写自己的简化版本的vector.简化处在于没有使用模板,只用于string.
首先对于内存的管理应该使用allocator类来管理。注意许多成员的公共部分应该定义成私有部分,避免重复代码.
定义几个指针用于指向动态内存的有效元素范围与分配的内存范围.
#include "iostream"
#include "string"
#include "memory"
#include "utility"
#include "map"
#include "vector"
using namespace std;
class StrVec
{
public:
StrVec() :elements(nullptr), first_free(nullptr), cap(nullptr){}
StrVec(const StrVec&);//拷贝构造函数
StrVec& operator=(const StrVec &);//拷贝赋值运算符
~StrVec();//析构函数
void push_back( const string&);
size_t size() const { return first_free - elements; }//返回大小
size_t capcity() const { return cap - elements; }//返回分配内存大小
void reserve(size_t );
void resize(size_t);
void resize(size_t,const string&);//重载resize,初始化
string * begin() const { return elements; }
string *end() const { return first_free; }
private:
static allocator<string> alloc;//用来分配管理动态内存
void check_n_alloc(){ if (size() == capcity()) reallocate(); };//如果内存已满,重新分配新的内存并移动元素
pair<string *, string *> alloc_n_copy(const string *, const string *);//分配内存并拷贝给定元素到新分配的内存
void free();//销毁元素并释放动态分配的内存
void remove_from(size_t);
void reallocate();//获得更多内存并拷贝已有元素
void alloc_n_move(size_t);
string * elements;//指向首元素的指针
string* first_free;//指向第一个空闲元素
string* cap;//指向数组尾后位置
};
/* public实现部分 */
void StrVec::resize(size_t new_size)
{
resize(new_size, string());
}
void StrVec::resize(size_t new_size,const string &s)
{
if (new_size > size())//新的大小大于原来大小,必须在原来的末尾添加新的元素
for (size_t i = size(); i != new_size; ++i)
push_back(s);
else //新的大小小于原来大小,删除一些元素
remove_from(size() - new_size);
}
void StrVec::reserve(size_t n)//保留至少大于n的容量
{
if (n > capcity())//需求大于当前容量,否则返回,不退回内存
alloc_n_move(n);
}
void StrVec::push_back(const string&s)
{
check_n_alloc();
alloc.construct(first_free++, s);//注意迭代器需要自增
}
StrVec::StrVec(const StrVec& S)
{
auto p = alloc_n_copy(S.begin(), S.end());//拷贝右侧对象的元素
elements = p.first;
first_free = cap = p.second;
}
StrVec& StrVec:: operator=(const StrVec& S)
{
auto p = alloc_n_copy(S.begin(), S.end());
free();
elements = p.first;
first_free = cap = p.second;
return *this;
}
StrVec::~StrVec()//析构函数
{
free();
}
/**************** private: *********************** */
void StrVec::reallocate()//重新分配内存并移动原来的元素
{
auto newcapcity = size() ? 2 * size() : 1;
alloc_n_move(newcapcity);
}
void StrVec::free()
{
if (elements)//elements不为空
{
for (auto p = first_free; p != elements;)//按照递减的方式删除元素
alloc.destroy(--p);
alloc.deallocate(elements, capcity());//释放内存
}
}
void StrVec::remove_from(size_t n)//删除后面的n个元素,不需要释放内存
{
for (auto p = first_free; p != first_free - n;)//按照递减的方式删除元素
alloc.destroy(--p);
}
void StrVec::alloc_n_move(size_t newcapcity)
{
auto newdata = alloc.allocate(newcapcity);//分配比原来大2倍的空间,注意原来的空间内存为0的特殊情况
auto dest = newdata;
auto elem = elements;
for (size_t i = 0; i != size(); ++i)
alloc.construct(dest++, std::move(*elem++)); //通过size()次循环将元素移动
free();//释放旧的内存空间
elements = newdata;
first_free = dest;
cap = newdata + newcapcity;
}
pair<string *, string *> StrVec::alloc_n_copy(const string * b, const string * e)
{
auto data = alloc.allocate(e - b);//分配内存
return{ data, uninitialized_copy(b, e, data) };//返回第一个迭代器和最后一个迭代器
}
allocator<string> StrVec:: alloc;//在类外定义静态变量
int main()
{
StrVec s;
s.reserve(5);
s.push_back("ssss");
s.push_back("aaaa");
s.push_back("mmmmmmmm");
s.resize(10, "xxx");
for (auto beg = s.begin(); beg != s.end(); ++beg)
cout << *beg << endl;
return 0;
}
======================================================================================
增加一个构造函数,用initializer_list作为参数.
StrVec::StrVec(initializer_list<string> il)
{
for (auto s : il)
push_back(s);
}
======================================================================================================================
重写free成员,用for_each和lambada来代替for循环,哪种实现更好?
for_each(elements, first_free,[](const string p){alloc.destroy(&p);});
我认为第二种`更好,因为不需要估计递增与递减。