Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -----------------------------------------------------------------------
- vector.h
- -----------------------------------------------------------------------
- #pragma once
- #include <cassert>
- #include <memory>
- #include <algorithm>
- #include <cstdlib>
- #include <new>
- #include <utility>
- template <typename T>
- class RawMemory {
- public:
- RawMemory() = default;
- explicit RawMemory(size_t capacity) : buffer_(Allocate(capacity))
- , capacity_(capacity) {}
- ~RawMemory() {Deallocate(buffer_);}
- RawMemory(const RawMemory&) = delete;
- RawMemory& operator=(const RawMemory& rhs) = delete;
- RawMemory(RawMemory&& other) noexcept : buffer_(std::exchange(other.buffer_, nullptr))
- , capacity_(std::exchange(other.capacity_, 0)) {}
- RawMemory& operator=(RawMemory&& rhs) noexcept {
- if (this != &rhs) {
- buffer_ = std::move(rhs.buffer_);
- capacity_ = std::move(rhs.capacity_);
- rhs.buffer_ = nullptr;
- rhs.capacity_ = 0;
- }
- return *this;
- }
- T* operator+(size_t offset) noexcept {assert(offset <= capacity_); return buffer_ + offset;}
- const T* operator+(size_t offset) const noexcept {return const_cast<RawMemory&>(*this) + offset;}
- const T& operator[](size_t index) const noexcept {return const_cast<RawMemory&>(*this)[index];}
- T& operator[](size_t index) noexcept {assert(index < capacity_); return buffer_[index];}
- void Swap(RawMemory& other) noexcept {std::swap(buffer_, other.buffer_); std::swap(capacity_, other.capacity_);}
- const T* GetAddress() const noexcept {return buffer_;}
- T* GetAddress() noexcept {return buffer_;}
- size_t Capacity() const {return capacity_;}
- private:
- T* buffer_ = nullptr;
- size_t capacity_ = 0;
- static T* Allocate(size_t n) {return n != 0 ? static_cast<T*>(operator new(n * sizeof(T))) : nullptr;}
- static void Deallocate(T* buf) noexcept {operator delete(buf);}
- };
- template <typename T>
- class Vector {
- public:
- Vector() = default;
- explicit Vector(size_t size) : data_(size)
- , size_(size) {std::uninitialized_value_construct_n(data_.GetAddress(), size);}
- Vector(const Vector& other) : data_(other.size_)
- , size_(other.size_) {std::uninitialized_copy_n(other.data_.GetAddress(), size_, data_.GetAddress());}
- Vector(Vector&& other) noexcept : data_(std::move(other.data_))
- , size_(std::exchange(other.size_, 0)) {}
- ~Vector() {std::destroy_n(data_.GetAddress(), size_);}
- size_t Size() const noexcept {return size_;}
- size_t Capacity() const noexcept {return data_.Capacity();}
- void Swap(Vector& other) noexcept {data_.Swap(other.data_), std::swap(size_, other.size_);}
- void Reserve(size_t new_capacity) {
- if (new_capacity <= data_.Capacity()) {
- return;
- }
- RawMemory<T> new_data(new_capacity);
- if constexpr (std::is_nothrow_move_constructible_v<T> || !std::is_copy_constructible_v<T>) {
- std::uninitialized_move_n(data_.GetAddress(), size_, new_data.GetAddress());
- } else {
- std::uninitialized_copy_n(data_.GetAddress(), size_, new_data.GetAddress());
- }
- std::destroy_n(data_.GetAddress(), size_);
- data_.Swap(new_data);
- }
- void Resize(size_t new_size) {
- if (new_size < size_) {
- std::destroy_n(data_.GetAddress() + new_size, size_ - new_size);
- } else {
- if (new_size > data_.Capacity()) {
- const size_t new_capacity = std::max(data_.Capacity() * 2, new_size);
- Reserve(new_capacity);
- }
- std::uninitialized_value_construct_n(data_.GetAddress() + size_, new_size - size_);
- }
- size_ = new_size;
- }
- template <typename Type>
- void PushBack(Type&& value);
- template <typename... Args>
- T& EmplaceBack(Args&&... args);
- void PopBack() {
- assert(size_);
- std::destroy_at(data_.GetAddress() + size_ - 1);
- --size_;
- }
- Vector& operator=(const Vector& other) {
- if (this != &other) {
- if (other.size_ <= data_.Capacity()) {
- if (size_ <= other.size_) {
- std::copy(other.data_.GetAddress(),
- other.data_.GetAddress() + size_,
- data_.GetAddress());
- std::uninitialized_copy_n(other.data_.GetAddress() + size_,
- other.size_ - size_,
- data_.GetAddress() + size_);
- } else {
- std::copy(other.data_.GetAddress(),
- other.data_.GetAddress() + other.size_,
- data_.GetAddress());
- std::destroy_n(data_.GetAddress() + other.size_,
- size_ - other.size_);
- }
- size_ = other.size_;
- } else {
- Vector other_copy(other);
- Swap(other_copy);
- }
- }
- return *this;
- }
- Vector& operator=(Vector&& other) noexcept {Swap(other); return *this;}
- const T& operator[](size_t index) const noexcept {return const_cast<Vector&>(*this)[index];}
- T& operator[](size_t index) noexcept {assert(index < size_); return data_[index];}
- private:
- RawMemory<T> data_;
- size_t size_ = 0;
- };
- template <typename T>
- template <typename Type>
- void Vector<T>::PushBack(Type&& value) {
- if (data_.Capacity() <= size_) {
- RawMemory<T> new_data(size_ == 0 ? 1 : size_ * 2);
- new (new_data.GetAddress() + size_) T(std::forward<Type>(value));
- if constexpr (std::is_nothrow_move_constructible_v<T> || !std::is_copy_constructible_v<T>) {
- std::uninitialized_move_n(data_.GetAddress(), size_, new_data.GetAddress());
- } else {
- std::uninitialized_copy_n(data_.GetAddress(), size_, new_data.GetAddress());
- }
- std::destroy_n(data_.GetAddress(), size_);
- data_.Swap(new_data);
- } else {
- new (data_.GetAddress() + size_) T(std::forward<Type>(value));
- }
- size_++;
- }
- template <typename T>
- template <typename... Args>
- T& Vector<T>::EmplaceBack(Args&&... args) {
- if (data_.Capacity() <= size_) {
- RawMemory<T> new_data(size_ == 0 ? 1 : size_ * 2);
- new (new_data.GetAddress() + size_) T(std::forward<Args>(args)...);
- if constexpr (std::is_nothrow_move_constructible_v<T> || !std::is_copy_constructible_v<T>) {
- std::uninitialized_move_n(data_.GetAddress(), size_, new_data.GetAddress());
- } else {
- std::uninitialized_copy_n(data_.GetAddress(), size_, new_data.GetAddress());
- }
- std::destroy_n(data_.GetAddress(), size_);
- data_.Swap(new_data);
- } else {
- new (data_.GetAddress() + size_) T(std::forward<Args>(args)...);
- }
- return data_[size_++];
- }
- -----------------------------------------------------------------------
- main.cpp
- -----------------------------------------------------------------------
- #include "vector.h"
- #include <iostream>
- #include <stdexcept>
- #include <string>
- 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(int id, std::string name) : id(id)
- , name(std::move(name)) {++num_constructed_with_id_and_name;}
- 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_constructed_with_id_and_name
- - 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;
- num_constructed_with_id_and_name = 0;
- }
- bool throw_on_copy = false;
- int id = 0;
- std::string name;
- 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_constructed_with_id_and_name = 0;
- static inline int num_copied = 0;
- static inline int num_moved = 0;
- static inline int num_destroyed = 0;
- };
- }//end 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 (...) {
- 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 (...) {
- 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 (...) {
- 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());
- v.PushBack(v[0]);
- assert(v[0].IsAlive());
- assert(v[1].IsAlive());
- }
- {
- Vector<TestObj> v(1);
- assert(v.Size() == v.Capacity());
- v.PushBack(std::move(v[0]));
- assert(v[0].IsAlive());
- assert(v[1].IsAlive());
- }
- }
- void Test5() {
- const int ID = 42;
- using namespace std::literals;
- {
- Obj::ResetCounters();
- Vector<Obj> v;
- auto& elem = v.EmplaceBack(ID, "Ivan"s);
- assert(v.Capacity() == 1);
- assert(v.Size() == 1);
- assert(&elem == &v[0]);
- assert(v[0].id == ID);
- assert(v[0].name == "Ivan"s);
- assert(Obj::num_constructed_with_id_and_name == 1);
- assert(Obj::GetAliveObjectCount() == 1);
- }
- assert(Obj::GetAliveObjectCount() == 0);
- {
- Vector<TestObj> v(1);
- assert(v.Size() == v.Capacity());
- v.EmplaceBack(v[0]);
- assert(v[0].IsAlive());
- assert(v[1].IsAlive());
- }
- }
- int main() {
- try {
- Test1();
- Test2();
- Test3();
- Test4();
- Test5();
- } catch (const std::exception& e) {
- std::cerr << e.what() << std::endl;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment