练习 16.16:将StrVec类重写为模板,命名为Vec。
代码:
#include <algorithm>
#include <string>
#include <stdexcept>
template <typename T> class Vec{
friend bool operator==(const Vec<T> &lhs, const Vec<T> &rhs) {
if (lhs.size() != rhs.size())
return false;
auto p = lhs.elements, p1 = rhs.elements;
while (p != lhs.first_free) {
if (*p++ != *p1++) return false;
}
return true;
}
friend bool operator!=(const Vec<T> &lhs, const Vec<T> &rhs) {
return !(lhs == rhs);
}
friend bool operator<(const Vec<T> &lhs, const Vec<T> &rhs) {
auto p = lhs.elements, p1 = rhs.elements;
while (p != lhs.end() && p1 != rhs.end()) {
if (*p != *p1) return *p < *p1;
++p, ++p1;
}
return p == lhs.end() && p1 != rhs.end() ? true : false;
}
public:
Vec():
elements(nullptr), first_free(nullptr), cap(nullptr) { }
Vec(const Vec &);
Vec(Vec &&) noexcept;
Vec(const std::initializer_list<T> &);
Vec(std::size_t n);
Vec(std::size_t n, const T &val);
~Vec();
Vec &operator=(const Vec &);
Vec &operator=(Vec &&) noexcept;
Vec &operator=(const std::initializer_list<T> &);
T &operator[](std::size_t n) {
check(n, "out of range");
return *(elements + n);
}
const T &operator[](std::size_t n) const {
check(n, "out of range");
return *(elements + n);
}
void push_back(const T &);
size_t size() const {
return first_free - elements;
}
size_t capacity() const {
return cap - elements;
}
void reserve(std::size_t);
void resize(std::size_t);
void resize(std::size_t, const T &);
void construct_each(T *&, T *, const T &);
T *begin()const {
return elements;
}
T *end()const {
return first_free;
}
private:
static std::allocator<T> alloc;
void chk_n_alloc() {
if (size() == capacity())
reallocate();
}
std::pair<T *, T *> alloc_n_copy(const T *, const T *);
void free();
void reallocate();
void allocate_and_construct(std::size_t);
void check(std::size_t i, const std::string &msg) const;
T *elements;
T *first_free;
T *cap;
};
template<typename T>
Vec<T>::Vec(const Vec &v) {
auto newdata = alloc_n_copy(v.begin(), v.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
template<typename T>
Vec<T>::Vec(Vec &&v) noexcept :
elements(v.elements), first_free(v.first_free), cap(v.cap) {
v.elements = v.first_free = v.cap = nullptr;
}
template<typename T>
Vec<T>::Vec(const std::initializer_list<T> &il) {
auto newdata = alloc_n_copy(il.begin(), il.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
template<typename T>
Vec<T>::Vec(std::size_t n) : Vec(n, T()) { }
template<typename T>
Vec<T>::Vec(std::size_t n, const T &val) {
allocate_and_construct(n);
construct_each(first_free, elements + n, val);
}
template<typename T>
Vec<T>::~Vec() {
free();
}
template<typename T>
Vec<T> &Vec<T>::operator=(const Vec &v) {
auto data = alloc_n_copy(v.begin(), v.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
template<typename T>
Vec<T> &Vec<T>::operator=(Vec &&v) noexcept{
if (this != &v) {
free();
elements = v.elements;
first_free = v.first_free;
cap = v.cap;
v.elements = v.first_free = v.cap = nullptr;
}
}
template<typename T>
Vec<T> &Vec<T>::operator=(const std::initializer_list<T> &il) {
auto data = alloc_n_copy(il.begin(), il.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
template<typename T>
void Vec<T>::push_back(const T &val) {
chk_n_alloc();
alloc.construct(first_free++, val);
}
template<typename T>
void Vec<T>::reserve(std::size_t n) {
if (n > capacity()) {
allocate_and_construct(n);
}
}
template<typename T>
void Vec<T>::resize(std::size_t n) {
resize(n, T());
}
template<typename T>
void Vec<T>::resize(std::size_t n, const T &val) {
if (n < size()) {
while (first_free != elements + n) {
alloc.destroy(--first_free);
}
} else if (n > capacity()) {
allocate_and_construct((3 * n + 1) / 2);
construct_each(first_free, elements + n, val);
} else if (n > size()) {
construct_each(first_free, elements + n, val);
}
}
template<typename T>
void Vec<T>::construct_each(T *&begin, T *end, const T &newVal) {
uninitialized_fill(begin, end, newVal);
begin = end;
}
template<typename T>
std::pair<T *, T *> Vec<T>::alloc_n_copy(const T *b, const T *e) {
auto data = alloc.allocate(e - b);
return { data, uninitialized_copy(b, e, data) };
}
template<typename T>
void Vec<T>::free() {
if (elements) {
for (auto p = first_free; p != elements;) {
alloc.destroy(--p);
}
alloc.deallocate(elements, cap - elements);
}
}
template<typename T>
void Vec<T>::reallocate() {
auto newcapacity = size() ? (3 * size() + 1) / 2 : 1;
allocate_and_construct(newcapacity);
}
template<typename T>
void Vec<T>::allocate_and_construct(std::size_t newcapacity) {
auto first = alloc.allocate(newcapacity);
auto last = std::uninitialized_copy(std::make_move_iterator(begin()), std::make_move_iterator(end()), first);
free();
elements = first;
first_free = last;
cap = elements + newcapacity;
}
template<typename T>
void Vec<T>::check(std::size_t i, const std::string &msg) const {
if (size() <= i) {
throw std::out_of_range(msg);
}
}