Untitled
unknown
plain_text
2 years ago
11 kB
3
Indexable
Never
#include "vector.h" #include <stdexcept> namespace { // Магическое число, используемое для отслеживания живости объекта inline const uint32_t DEFAULT_COOKIE = 0xdeadbeef; struct TestObj { TestObj() = default; TestObj(const TestObj& other) = default; TestObj& operator=(const TestObj& other) = default; TestObj(TestObj&& other) = default; TestObj& operator=(TestObj&& other) = default; ~TestObj() { cookie = 0; } [[nodiscard]] bool IsAlive() const noexcept { return cookie == DEFAULT_COOKIE; } uint32_t cookie = DEFAULT_COOKIE; }; struct Obj { Obj() { if (default_construction_throw_countdown > 0) { if (--default_construction_throw_countdown == 0) { throw std::runtime_error("Oops"); } } ++num_default_constructed; } explicit Obj(int id) : id(id) // { ++num_constructed_with_id; } Obj(const Obj& other) : id(other.id) // { if (other.throw_on_copy) { throw std::runtime_error("Oops"); } ++num_copied; } Obj(Obj&& other) noexcept : id(other.id) // { ++num_moved; } Obj& operator=(const Obj& other) = default; Obj& operator=(Obj&& other) = default; ~Obj() { ++num_destroyed; id = 0; } static int GetAliveObjectCount() { return num_default_constructed + num_copied + num_moved + num_constructed_with_id - num_destroyed; } static void ResetCounters() { default_construction_throw_countdown = 0; num_default_constructed = 0; num_copied = 0; num_moved = 0; num_destroyed = 0; num_constructed_with_id = 0; } bool throw_on_copy = false; int id = 0; static inline int default_construction_throw_countdown = 0; static inline int num_default_constructed = 0; static inline int num_constructed_with_id = 0; static inline int num_copied = 0; static inline int num_moved = 0; static inline int num_destroyed = 0; }; } // namespace void Test1() { Obj::ResetCounters(); const size_t SIZE = 100500; const size_t INDEX = 10; const int MAGIC = 42; { Vector<int> v; assert(v.Capacity() == 0); assert(v.Size() == 0); v.Reserve(SIZE); assert(v.Capacity() == SIZE); assert(v.Size() == 0); } { Vector<int> v(SIZE); const auto& cv(v); assert(v.Capacity() == SIZE); assert(v.Size() == SIZE); assert(v[0] == 0); assert(&v[0] == &cv[0]); v[INDEX] = MAGIC; assert(v[INDEX] == MAGIC); assert(&v[100] - &v[0] == 100); v.Reserve(SIZE * 2); assert(v.Size() == SIZE); assert(v.Capacity() == SIZE * 2); assert(v[INDEX] == MAGIC); } { Vector<int> v(SIZE); v[INDEX] = MAGIC; const auto v_copy(v); assert(&v[INDEX] != &v_copy[INDEX]); assert(v[INDEX] == v_copy[INDEX]); } { Vector<Obj> v; v.Reserve(SIZE); assert(Obj::GetAliveObjectCount() == 0); } { Vector<Obj> v(SIZE); assert(Obj::GetAliveObjectCount() == SIZE); const int old_copy_count = Obj::num_copied; const int old_move_count = Obj::num_moved; v.Reserve(SIZE * 2); assert(Obj::GetAliveObjectCount() == SIZE); assert(Obj::num_copied == old_copy_count); assert(Obj::num_moved == old_move_count + static_cast<int>(SIZE)); } assert(Obj::GetAliveObjectCount() == 0); } void Test2() { const size_t SIZE = 100; Obj::ResetCounters(); { Obj::default_construction_throw_countdown = SIZE / 2; try { Vector<Obj> v(SIZE); assert(false && "Exception is expected"); } catch (const std::runtime_error&) { } catch (...) { // Unexpected error assert(false && "Unexpected exception"); } assert(Obj::num_default_constructed == SIZE / 2 - 1); assert(Obj::GetAliveObjectCount() == 0); } Obj::ResetCounters(); { Vector<Obj> v(SIZE); try { v[SIZE / 2].throw_on_copy = true; Vector<Obj> v_copy(v); assert(false && "Exception is expected"); } catch (const std::runtime_error&) { assert(Obj::num_copied == SIZE / 2); } catch (...) { // Unexpected error assert(false && "Unexpected exception"); } assert(Obj::GetAliveObjectCount() == SIZE); } Obj::ResetCounters(); { Vector<Obj> v(SIZE); try { v[SIZE - 1].throw_on_copy = true; v.Reserve(SIZE * 2); } catch (...) { // Unexpected error assert(false && "Unexpected exception"); } assert(v.Capacity() == SIZE * 2); assert(v.Size() == SIZE); assert(Obj::GetAliveObjectCount() == SIZE); } } void Test3() { const size_t MEDIUM_SIZE = 100; const size_t LARGE_SIZE = 250; const int ID = 42; { Obj::ResetCounters(); Vector<int> v(MEDIUM_SIZE); { auto v_copy(std::move(v)); assert(v_copy.Size() == MEDIUM_SIZE); assert(v_copy.Capacity() == MEDIUM_SIZE); } assert(Obj::GetAliveObjectCount() == 0); } { Obj::ResetCounters(); { Vector<Obj> v(MEDIUM_SIZE); v[MEDIUM_SIZE / 2].id = ID; assert(Obj::num_default_constructed == MEDIUM_SIZE); Vector<Obj> moved_from_v(std::move(v)); assert(moved_from_v.Size() == MEDIUM_SIZE); assert(moved_from_v[MEDIUM_SIZE / 2].id == ID); } assert(Obj::GetAliveObjectCount() == 0); assert(Obj::num_moved == 0); assert(Obj::num_copied == 0); assert(Obj::num_default_constructed == MEDIUM_SIZE); } { Obj::ResetCounters(); Vector<Obj> v_medium(MEDIUM_SIZE); v_medium[MEDIUM_SIZE / 2].id = ID; Vector<Obj> v_large(LARGE_SIZE); v_large = v_medium; assert(v_large.Size() == MEDIUM_SIZE); assert(v_large.Capacity() == LARGE_SIZE); assert(v_large[MEDIUM_SIZE / 2].id == ID); assert(Obj::GetAliveObjectCount() == MEDIUM_SIZE + MEDIUM_SIZE); } { Obj::ResetCounters(); Vector<Obj> v(MEDIUM_SIZE); { Vector<Obj> v_large(LARGE_SIZE); v_large[LARGE_SIZE - 1].id = ID; v = v_large; assert(v.Size() == LARGE_SIZE); assert(v_large.Capacity() == LARGE_SIZE); assert(v_large[LARGE_SIZE - 1].id == ID); assert(Obj::GetAliveObjectCount() == LARGE_SIZE + LARGE_SIZE); } assert(Obj::GetAliveObjectCount() == LARGE_SIZE); } assert(Obj::GetAliveObjectCount() == 0); { Obj::ResetCounters(); Vector<Obj> v(MEDIUM_SIZE); v[MEDIUM_SIZE - 1].id = ID; Vector<Obj> v_small(MEDIUM_SIZE / 2); v_small.Reserve(MEDIUM_SIZE + 1); const size_t num_copies = Obj::num_copied; v_small = v; assert(v_small.Size() == v.Size()); assert(v_small.Capacity() == MEDIUM_SIZE + 1); v_small[MEDIUM_SIZE - 1].id = ID; assert(Obj::num_copied - num_copies == MEDIUM_SIZE - (MEDIUM_SIZE / 2)); } } void Test4() { const size_t ID = 42; const size_t SIZE = 100'500; { Obj::ResetCounters(); Vector<Obj> v; v.Resize(SIZE); assert(v.Size() == SIZE); assert(v.Capacity() == SIZE); assert(Obj::num_default_constructed == SIZE); } assert(Obj::GetAliveObjectCount() == 0); { const size_t NEW_SIZE = 10'000; Obj::ResetCounters(); Vector<Obj> v(SIZE); v.Resize(NEW_SIZE); assert(v.Size() == NEW_SIZE); assert(v.Capacity() == SIZE); assert(Obj::num_destroyed == SIZE - NEW_SIZE); } assert(Obj::GetAliveObjectCount() == 0); { Obj::ResetCounters(); Vector<Obj> v(SIZE); Obj o{ID}; v.PushBack(o); assert(v.Size() == SIZE + 1); assert(v.Capacity() == SIZE * 2); assert(v[SIZE].id == ID); assert(Obj::num_default_constructed == SIZE); assert(Obj::num_copied == 1); assert(Obj::num_constructed_with_id == 1); assert(Obj::num_moved == SIZE); } assert(Obj::GetAliveObjectCount() == 0); { Obj::ResetCounters(); Vector<Obj> v(SIZE); v.PushBack(Obj{ID}); assert(v.Size() == SIZE + 1); assert(v.Capacity() == SIZE * 2); assert(v[SIZE].id == ID); assert(Obj::num_default_constructed == SIZE); assert(Obj::num_copied == 0); assert(Obj::num_constructed_with_id == 1); assert(Obj::num_moved == SIZE + 1); } { Obj::ResetCounters(); Vector<Obj> v; v.PushBack(Obj{ID}); v.PopBack(); assert(v.Size() == 0); assert(v.Capacity() == 1); assert(Obj::GetAliveObjectCount() == 0); } { Vector<TestObj> v(1); assert(v.Size() == v.Capacity()); // Операция PushBack существующего элемента вектора должна быть безопасна // даже при реаллокации памяти v.PushBack(v[0]); assert(v[0].IsAlive()); assert(v[1].IsAlive()); } { Vector<TestObj> v(1); assert(v.Size() == v.Capacity()); // Операция PushBack для перемещения существующего элемента вектора должна быть безопасна // даже при реаллокации памяти v.PushBack(std::move(v[0])); assert(v[0].IsAlive()); assert(v[1].IsAlive()); } } int main() { Test1(); Test2(); Test3(); Test4(); }