1. Objetivo
Implemente una versión simplificada de la clase de vector de biblioteca estándar, que solo admite cadenas, la llamamos StrVec.
2. Ideas de diseño
2.1 El asignador administra la reserva de memoria de cada objeto StrVec, que es una memoria contigua (una matriz de tipos). Elementos utilizados para construir insertos de almacenamiento.
push_back插入元素, 检查内存池是否够用?
. 够用,在内存池的下—可用位置构造对象·
. 不够用,重组空间:
. allocator获取全新的更大的内存池
. 将已有元素拷贝至新的内存池
. 释放旧的内存池
. 在新内存池的下—可用位置构造新加入的元素
2.2 Cuatro funciones de la herramienta:
alloc_n_copy asignará memoria y copiará los elementos en un rango dado.
el libre albedrío destruye el elemento construido y libera la memoria.
chk.
reasignar asigna nueva memoria para StrVec cuando la memoria se agota.
3. StrVec.h
class StrVec {
public:
// copy control members
StrVec():
elements(nullptr), first_free(nullptr), cap(nullptr) {
}
StrVec(const StrVec&); // copy constructor
StrVec &operator=(const StrVec&); // copy assignment
#ifdef NOEXCEPT
StrVec(StrVec&&) noexcept; // move constructor
StrVec &operator=(StrVec&&) noexcept; // move assignment
~StrVec() noexcept; // destructor
#else
StrVec(StrVec&&) throw(); // move constructor
StrVec &operator=(StrVec&&) throw(); // move assignment
~StrVec() throw(); // destructor
#endif
#ifdef INIT_LIST
// additional constructor
StrVec(std::initializer_list<std::string>);
#else // define a constructor that takes pointers to a range of elements
StrVec(const std::string*, const std::string*);
#endif
void push_back(const std::string&); // copy the element
void push_back(std::string&&); // move the element
// add elements
size_t size() const {
return first_free - elements; }
size_t capacity() const {
return cap - elements; }
// iterator interface
std::string *begin() const {
return elements; }
std::string *end() const {
return first_free; }
#ifdef INIT_LIST // no real substitute for initializer_list in assignments
// operator functions covered in chapter 14
StrVec &operator=(std::initializer_list<std::string>);
#endif
std::string& operator[](std::size_t n)
{
return elements[n]; }
const std::string& operator[](std::size_t n) const
{
return elements[n]; }
#ifdef VARIADICS // no direct substitute for variadic functions
// emplace member covered in chapter 16
template <class... Args> void emplace_back(Args&&...);
#endif
private:
static std::allocator<std::string> alloc; // allocates the elements
// utility functions:
// used by members that add elements to the StrVec
void chk_n_alloc()
{
if (size() == capacity()) reallocate(); }
// used by the copy constructor, assignment operator, and destructor
std::pair<std::string*, std::string*> alloc_n_copy
(const std::string*, const std::string*);
void free(); // destroy the elements and free the space
void reallocate(); // get more space and copy the existing elements
std::string *elements; // pointer to the first element in the array
std::string *first_free; // pointer to the first free element in the array
std::string *cap; // pointer to one past the end of the array
};
#include <algorithm>
inline
#ifdef NOEXCEPT
StrVec::~StrVec() noexcept {
free(); }
#else
StrVec::~StrVec() throw() {
free(); }
#endif
4. StrVec.cpp
inline
std::pair<std::string*, std::string*>
StrVec::alloc_n_copy(const std::string *b, const std::string *e)
{
// allocate space to hold as many elements as are in the range
auto data = alloc.allocate(e - b);
// initialize and return a pair constructed from data and
// the value returned by uninitialized_copy
#ifdef LIST_INIT
return {
data, uninitialized_copy(b, e, data)};
#else
return make_pair(data, uninitialized_copy(b, e, data));
#endif
}
inline
#ifdef NOEXCEPT
StrVec::StrVec(StrVec &&s) noexcept // move won't throw any exceptions
#else
StrVec::StrVec(StrVec &&s) throw() // move won't throw any exceptions
#endif
// member initializers take over the resources in s
: elements(s.elements), first_free(s.first_free), cap(s.cap)
{
// leave s in a state in which it is safe to run the destructor
s.elements = s.first_free = s.cap = nullptr;
}
inline StrVec::StrVec(const StrVec &s)
{
// call alloc_n_copy to allocate exactly as many elements as in s
auto newdata = alloc_n_copy(s.begin(), s.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
inline void StrVec::free()
{
// may not pass deallocate a 0 pointer; if elements is 0, there's no work to do
if (elements) {
// destroy the old elements in reverse order
for (auto p = first_free; p != elements; /* empty */)
alloc.destroy(--p);
alloc.deallocate(elements, cap - elements);
}
}
#ifdef INIT_LIST
inline StrVec &StrVec::operator=(std::initializer_list<std::string> il)
{
// alloc_n_copy allocates space and copies elements from the given range
auto data = alloc_n_copy(il.begin(), il.end());
free(); // destroy the elements in this object and free the space
elements = data.first; // update data members to point to the new space
first_free = cap = data.second;
return *this;
}
#endif
inline
#ifdef NOEXCEPT
StrVec &StrVec::operator=(StrVec &&rhs) noexcept
#else
StrVec &StrVec::operator=(StrVec &&rhs) throw()
#endif
{
// direct test for self-assignment
if (this != &rhs) {
free(); // free existing elements
elements = rhs.elements; // take over resources from rhs
first_free = rhs.first_free;
cap = rhs.cap;
// leave rhs in a destructible state
rhs.elements = rhs.first_free = rhs.cap = nullptr;
}
return *this;
}
inline StrVec &StrVec::operator=(const StrVec &rhs)
{
// call alloc_n_copy to allocate exactly as many elements as in rhs
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
inline void StrVec::reallocate()
{
// we'll allocate space for twice as many elements as the current size
auto newcapacity = size() ? 2 * size() : 1;
// allocate new memory
auto newdata = alloc.allocate(newcapacity);
// move the data from the old memory to the new
auto dest = newdata; // points to the next free position in the new array
auto elem = elements; // points to the next element in the old array
for (size_t i = 0; i != size(); ++i)
alloc.construct(dest++, std::move(*elem++));
free(); // free the old space once we've moved the elements
// update our data structure to point to the new elements
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
#ifdef INIT_LIST
inline StrVec::StrVec(std::initializer_list<std::string> il)
{
// call alloc_n_copy to allocate exactly as many elements as in il
auto newdata = alloc_n_copy(il.begin(), il.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
#else
inline StrVec::StrVec(const std::string *b, const std::string* e)
{
// call alloc_n_copy to allocate exactly as many elements as in the range
auto newdata = alloc_n_copy(b, e);
elements = newdata.first;
first_free = cap = newdata.second;
}
#endif
inline void StrVec::push_back(const std::string& s)
{
chk_n_alloc(); // ensure that there is room for another element
// construct a copy of s in the element to which first_free points
alloc.construct(first_free++, s);
}
inline void StrVec::push_back(std::string &&s)
{
chk_n_alloc(); // reallocates the StrVec if necessary
alloc.construct(first_free++, std::move(s));
}
#ifdef VARIADICS // no direct substitute for variadic functions
// emplace member covered in chapter 16
template <class... Args>
inline void StrVec::emplace_back(Args&&... args)
{
chk_n_alloc(); // reallocates the StrVec if necessary
alloc.construct(first_free++, std::forward<Args>(args)...);
}
#endif
#endif
【referencia】
[1] Código StrVec.h