Vec.h
#pragma once
#include <string>
#include <memory>
template <typename T>
class Vec
{
public:
Vec();
Vec(const Vec<T> &);
Vec &operator=(const Vec<T> &);
Vec(Vec<T> &&) noexcept;
Vec &operator=(Vec<T> &&) noexcept;
~Vec();
void push_back(const T &s);
size_t size() const;
size_t capacity() const;
T *begin() const;
T *end() const;
void reserve(size_t n);
void resize(size_t n, const T &s);
void push_back(T &&s);
private:
void check_volume();
void reallocate();
void free();
std::pair<T *, T *>
alloc_n_copy(const T *, const T *);
T *elements;
T *first_free;
T *cap;
static std::allocator<T> alloc;
};
template <typename T>
std::allocator<T> Vec<T>::alloc = std::allocator<T>();
template <typename T>
Vec<T>::Vec()
: elements(nullptr), first_free(nullptr), cap(nullptr) {
std::cout << "Default constructor" << std::endl;
}
template <typename T>
Vec<T>::Vec(const Vec &s) {
std::cout << "Copy constructor" << std::endl;
auto newBegin = alloc_n_copy(s.begin(), s.end());
elements = newBegin.first;
cap = first_free = newBegin.second;
}
template <typename T>
Vec<T> &Vec<T>::operator=(const Vec &rhs) {
std::cout << "Assign operator" << std::endl;
auto newBegin = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = newBegin.first;
cap = first_free = newBegin.second;
return *this;
}
template <typename T>
Vec<T>::Vec(Vec<T> &&s) noexcept {
std::cout << "Move constructor" << std::endl;
elements = s.elements;
first_free = s.first_free;
cap = s.cap;
s.elements = s.first_free = s.cap = nullptr;
}
template <typename T>
Vec<T> &Vec<T>::operator=(Vec<T> &&rhs) noexcept {
std::cout << "Move operator" << std::endl;
if (this != &rhs) {
free();
elements = rhs.elements;
first_free = rhs.first_free;
cap = rhs.cap;
rhs.elements = rhs.first_free = rhs.cap = nullptr;
}
return *this;
}
template <typename T>
Vec<T>::~Vec() {
free();
}
template <typename T>
void Vec<T>::push_back(T &&s) {
check_volume();
alloc.construct(first_free++, std::move(s));
}
template <typename T>
std::pair<T *, T *>
Vec<T>::alloc_n_copy(const T *b, const T *e) {
auto newMem = alloc.allocate(e - b);
return{ newMem, std::uninitialized_copy(b, e, newMem) };
}
template <typename T>
void Vec<T>::reallocate() {
size_t newSize = size() ? 2 * size() : 1;
auto first = alloc.allocate(newSize);
auto last = uninitialized_copy(make_move_iterator(begin()),
make_move_iterator(end()),
first);
free();
elements = first;
first_free = last;
cap = first + newSize;
}
template <typename T>
size_t Vec<T>::size() const {
return first_free - elements;
}
template <typename T>
size_t Vec<T>::capacity() const {
return cap - elements;
}
template <typename T>
void Vec<T>::push_back(const T &s) {
check_volume();
alloc.construct(first_free++, s);
}
template <typename T>
T *Vec<T>::begin() const {
return elements;
}
template <typename T>
T *Vec<T>::end() const {
return first_free;
}
template <typename T>
void Vec<T>::check_volume() {
if (size() == capacity()) reallocate();
}
template <typename T>
void Vec<T>::free() {
if (elements) {
for (auto p = first_free; p != elements;) {
alloc.destroy(--p); // should be destroy(--p); p point to the position after last elements, destroy a uninitialize pointer will lead to undefined behavior
}
alloc.deallocate(elements, cap - elements);
}
}
template <typename T>
void Vec<T>::reserve(size_t n) {
if (n > size()) {
std::string *newBegin = alloc.allocate(n);
std::string *new_first_free = std::uninitialized_copy(begin(), end(), newBegin);
free();
first_free = new_first_free;
elements = newBegin;
cap = newBegin + n;
}
}
template <typename T>
void Vec<T>::resize(size_t n, const T &s) {
std::string *newBegin = alloc.allocate(n);
std::string *new_first_free = nullptr;
if (n > size()) {
auto p = std::uninitialized_copy(begin(), end(), newBegin);
for (size_t i = 0; i < n - size(); ++i) {
alloc.construct(p++, s);
}
new_first_free = p;
}
else {
new_first_free = std::uninitialized_copy(begin(), begin() + n, newBegin);
}
first_free = new_first_free;
elements = newBegin;
cap = newBegin + n;
}
main.cpp
#include <iostream>
#include <string>
#include "Vec.h"
using namespace std;
int main()
{
Vec<string> a;
a.push_back("abc");
Vec<string> b;
cout << b.size() << endl;
b = a;
Vec<string> c(std::move(b));
a = std::move(c);
return 0;
}