练习13.39 编写你自己版本的StrVec,包括自己版本的reserve,capacity和resize。
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
class StrVec {
public:
StrVec() :elements(nullptr), first_free(nullptr), cap(nullptr) {}
StrVec(const StrVec&);
StrVec& operator=(const StrVec&);
~StrVec();
void push_pack(const std::string &);
size_t size()const { return first_free - elements; }
size_t capacity()const { return cap - elements; }
std::string *begin()const { return elements; }
std::string *end()const { return first_free; }
std::string* reverse(const size_t n) { auto ps = alloc.allocate(n); }
void resize(const size_t n)
{
if (n >= size()) {
alloc.allocate(n - size());
}
else {
//删除并释放大于n的内存
while ((first_free - elements) != size()) {
alloc.destroy(--first_free);
}
alloc.deallocate(first_free, size() - n);
}
}
private:
static std::allocator<std::string> alloc;
void check_n_alloc() { if (size() == capacity())reallocate(); }
std::pair<std::string*, std::string*> alloc_n_copy(const std::string*, const std::string*);
void free();
void reallocate();
std::string *elements;
std::string *first_free;
std::string *cap;
};
void StrVec::push_pack(const std::string &s)
{
check_n_alloc();
alloc.construct(first_free++, s);
}
pair<std::string*, std::string*> StrVec::alloc_n_copy(const std::string *b, const std::string *e)
{
auto data = alloc.allocate(e - b);
return{ data,uninitialized_copy(b,e,data) };
}
void StrVec::free()
{
if (elements) {
for (auto p = first_free; p != elements;) {
alloc.destroy(--p);
}
alloc.deallocate(elements, cap - elements);
}
}
StrVec::StrVec(const StrVec &s)
{
auto newdata = alloc_n_copy(s.begin(), s.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
StrVec::~StrVec()
{
free();
}
StrVec& StrVec::operator=(const StrVec& rhs)
{
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
void StrVec::reallocate()
{
auto newcapacity = size() ? 2 * size() : 1;
auto newdata = alloc.allocate(newcapacity);
auto dest = newdata;
auto elem = elements;
for (size_t i = 0; i != size(); ++i) {
alloc.construct(dest++, std::move(*elements++));
}
free();
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
//我的resize版本做的不好。贴一个作者的
void StrVec::reserve(size_t n){
if(n>capcity()){
auto newcapcity=n;
auto newdata=alloc.allocate(newcapcity);
auto old_e=elements;
auto dest=newdata;
for(size_t i=0;i<size();++i)
alloc.construct(dest++,std::move(*old_e++));
free();
elements=newdata;
first_free=dest;
cap=elements+newcapcity;
}
}
练习13.40 为你的StrVec类添加一个构造函数,它接受一个initializer_list<string>参数。
StrVec::StrVec(initializer_list<string> il)
{
auto newdata = alloc_n_copy(il.begin(), il.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
练习13.41 在push_back中,我们为什么在construct调用中使用后置递增运算?如果使用前置递增运算的话,会发生什么?
使用后置指针的目的是为了在当前位置插入元素,并将指针指向下一位置。
使用前置指针将导致插入的位置与设想的不符。
练习13.43 重写free成员,用for_each和lambda来代替循环destory元素。你更倾向于哪种实现,为什么。
void free()
{
for_each(elements, first_free, [](string& s) {alloc.destroy(&s); });
}
此版本是极好的,不降低可读性的情况让程序更简洁。