| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 | #include <cassert>#include <iostream>#include <vector>// this struct is used in the automated tests at the end of the file// it counts all instances with different numbers to ensure each object is// constructed and destructed the same amountstruct A {    static int instances;    int a;    A(int a) : a(a) {        // std::cout << "construct A " << a << "\n";        instances += a;    }    A(const A& other) : a(other.a) {        // std::cout << "copy construct A " << a << "\n";        instances += a;    }    A(A&& other) : a(other.a) {        // std::cout << "move construct A " << a << "\n";        instances += a;    }    A& operator=(const A& other) {        instances -= a;        a = other.a;        instances += a;        // std::cout << "copy assignment A " << a << "\n";        return *this;    }    A& operator=(A&& other) {        instances -= a;        a = other.a;        instances += a;        // std::cout << "move assignment A " << a << "\n";        return *this;    }    ~A() {        // std::cout << "destruct A " << a << "\n";        instances -= a;    }};int A::instances = 0;template<typename T>class Vector {    T* data;    int capacity;    int elements;public:    Vector() : data(nullptr), capacity(0), elements(0) {    }    ~Vector() {        // placement new needs explicit destructor calling        for(int i = 0; i < elements; i++) {            data[i].~T();        }        // casting this pointer not back to its origin type yields a crash        delete[] reinterpret_cast<char*>(data);    }    // allocate new storage for copies    Vector(const Vector& other)        : data(allocate(other.capacity)), capacity(other.capacity),          elements(other.elements) {        // copy data into new storage        for(int i = 0; i < elements; i++) {            new(data + i) T(other.data[i]);        }    }    // this handles copy and move assigment    // copy: replace current resources with other made by the copy constructor    // move: replace current resources with other made by the move constructor    // other gets the old data and is destroyed by the destructor after going    // out of scope    Vector& operator=(Vector other) {        swap(*this, other);        return *this;    }    // construct a default vector so the other vector gets valid values from the    // swap    Vector(Vector&& other) : Vector() {        swap(*this, other);    }    void reserve(int size) {        if(size <= capacity) {            return;        }        Vector v;        v.capacity = size;        v.data = allocate(size);        for(int i = 0; i < elements; i++) {            v.push_back(std::move(data[i]));        }        swap(*this, v);    }    void resize(int size, const T& t) {        // elements will be equal to size after this call but not the capacity        if(size > elements) {            // fill until the given size is reached            for(int i = elements; i < size; i++) {                push_back(t);            }        } else if(size < elements) {            // remove objects until the size matches            for(int i = size; i < elements; i++) {                data[i].~T();            }            elements = size;        }    }    void resize(int size) {        resize(size, T());    }    void push_back(const T& t) {        ensureCapacity();        new(data + elements++) T(t);    }    void push_back(T&& t) {        ensureCapacity();        // && would be lost without std::move        new(data + elements++) T(std::move(t));    }    T& operator[](int index) {        return data[index];    }    const T& operator[](int index) const {        return data[index];    }    T& at(int index) {        // std states "at" is [] with range check        assert(index >= 0 && index < elements);        return (*this)[index];    }    const T& at(int index) const {        // std states "at" is [] with range check        assert(index >= 0 && index < elements);        return (*this)[index];    }    int size() const {        return elements;    }    T* begin() {        return data;    }    T* end() {        return data + elements;    }    const T* begin() const {        return data;    }    const T* end() const {        return data + elements;    }    void erase(T* from, T* to) {        T* remove = from;        T* move = to;        T* stop = end();        // fill the hole by moving following objects as long as possible        while(move != stop) {            *remove = std::move(*move);            remove++;            move++;        }        // remove left over objects        while(remove != stop) {            remove->~T();            remove++;            elements--;        }    }    void erase(T* start) {        erase(start, start + 1);    }    T* as_array() {        return begin();    }    const T* as_array() const {        return begin();    }    void erase_by_swap(int index) {        elements--;        if(index != elements) {            data[index] = std::move(data[elements]);        }        data[elements].~T();    }private:    static T* allocate(int length) {        return reinterpret_cast<T*>(new char[sizeof(T) * length]);    }    static void swap(Vector& a, Vector& b) {        std::swap(a.data, b.data);        std::swap(a.capacity, b.capacity);        std::swap(a.elements, b.elements);    }    void ensureCapacity() {        if(elements >= capacity) {            // doubling the size amortizes costs            reserve(capacity == 0 ? 1 : capacity * 2);        }    }};void printError(int number) {    std::cout << "\033[0;31mError " << number << "\033[0m\n";}// V is either std::vector or Vector to test for complete same behaviour in both// implementationstemplate<typename V>void test() {    {        const int elements = 2;        V v;        for(int i = 0; i < elements; i++) {            v.push_back(A(i));        }        const V& cv = v;        for(int i = 0; i < elements; i++) {            if(v[i].a != i || cv[i].a != i || v.at(i).a != i ||               cv.at(i).a != i) {                printError(1);            }        }        if(v.size() != elements) {            printError(2);        }    }    {        V v1;        v1.push_back(A(10));        V v2 = v1;        V v3;        v3.push_back(A(20));        v3 = v1;        if(v1[0].a != 10 || v2[0].a != 10 || v3[0].a != 10) {            printError(3);        }        V v4 = std::move(v1);        V v5(std::move(v2));        V v6;        v6 = std::move(v3);        if(v1.size() != 0 || v2.size() != 0 || v3.size() != 0 ||           v4.size() != 1 || v5.size() != 1 || v6.size() != 1) {            printError(4);        }    }    {        V v;        v.resize(1, A(8));        v.resize(3, A(9));        if(v.size() != 3 || v[0].a != 8 || v[1].a != 9 || v[2].a != 9) {            printError(5);        }        v.resize(1, A(10));        if(v.size() != 1 || v[0].a != 8) {            printError(6);        }    }    {        std::vector<A> v1;        V v2;        for(int i = 0; i < 200; i++) {            v1.push_back(A(i));            v2.push_back(A(i));        }        v1.erase(v1.begin());        v1.erase(v1.begin() + 4, v1.begin() + 8);        v1.erase(v1.begin() + 30, v1.begin() + 56);        v2.erase(v2.begin());        v2.erase(v2.begin() + 4, v2.begin() + 8);        v2.erase(v2.begin() + 30, v2.begin() + 56);        if(static_cast<int>(v1.size()) != static_cast<int>(v2.size())) {            printError(100);        } else {            for(unsigned int i = 0; i < v1.size(); i++) {                if(v1[i].a != v2[i].a) {                    printError(200);                }            }        }    }    if(A::instances != 0) {        std::cout << "object counter is not 0: " << A::instances << "\n";    }}void test() {    {        Vector<A> v;        v.push_back(1);        v.push_back(2);        v.push_back(3);        v.erase_by_swap(1);        if(v.size() != 2 || v[0].a != 1 || v[1].a != 3) {            printError(9);        }        v.erase_by_swap(1);        if(v.size() != 1 || v[0].a != 1) {            printError(10);        }        v.erase_by_swap(0);        if(v.size() != 0) {            printError(11);        }    }    if(A::instances != 0) {        std::cout << "object counter is not 0: " << A::instances << "\n";    }}int main() {    test<Vector<A>>();    test();    std::cout << "--------------------------\n";    test<std::vector<A>>();}
 |