Untitled
unknown
plain_text
2 years ago
7.2 kB
10
Indexable
Never
#pragma once #include <algorithm> #include <cassert> #include <cstdlib> #include <memory> #include <new> #include <utility> template <typename T> class RawMemory { public: RawMemory() = default; explicit RawMemory(size_t capacity) : buffer_(Allocate(capacity)) , capacity_(capacity) { } RawMemory(const RawMemory&) = delete; RawMemory(RawMemory&& other) noexcept : buffer_(other.buffer_) , capacity_(other.capacity_) // { other.buffer_ = nullptr; other.capacity_ = 0; } ~RawMemory() { Deallocate(buffer_); } RawMemory& operator=(const RawMemory& rhs) = delete; RawMemory& operator=(RawMemory&& rhs) noexcept { Swap(rhs); 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: // Выделяет сырую память под n элементов и возвращает указатель на неё static T* Allocate(size_t n) { return n != 0 ? static_cast<T*>(operator new(n * sizeof(T))) : nullptr; } // Освобождает сырую память, выделенную ранее по адресу buf при помощи Allocate static void Deallocate(T* buf) noexcept { operator delete(buf); } T* buffer_ = nullptr; size_t capacity_ = 0; }; 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(), other.size_, data_.GetAddress()); } Vector(Vector&& other) noexcept : data_(std::move(other.data_)) , size_(other.Size()) // { other.size_ = 0; } Vector& operator=(const Vector& rhs) { if (this != &rhs) { if (rhs.size_ > data_.Capacity()) { Vector rhs_copy(rhs); Swap(rhs_copy); } else { const size_t copy_count = std::min(size_, rhs.size_); std::copy_n(rhs.data_.GetAddress(), copy_count, data_.GetAddress()); if (size_ < rhs.size_) { std::uninitialized_copy_n(rhs.data_ + size_, rhs.size_ - size_, data_ + size_); } else if (size_ > rhs.size_) { std::destroy_n(data_ + rhs.size_, size_ - rhs.size_); } size_ = rhs.size_; } } return *this; } Vector& operator=(Vector&& rhs) noexcept { data_.Swap(rhs.data_); std::swap(size_, rhs.size_); return *this; } ~Vector() { std::destroy_n(data_.GetAddress(), size_); } size_t Size() const noexcept { return size_; } size_t Capacity() const noexcept { return data_.Capacity(); } 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]; } void Reserve(size_t new_capacity) { if (new_capacity <= data_.Capacity()) { return; } RawMemory<T> new_data(new_capacity); UninitializedMoveNIfNoexcept(data_.GetAddress(), size_, new_data.GetAddress()); std::destroy_n(data_.GetAddress(), size_); data_.Swap(new_data); } void Resize(size_t new_size) { Reserve(new_size); if (size_ < new_size) { std::uninitialized_value_construct_n(data_ + size_, new_size - size_); } else if (size_ > new_size) { std::destroy_n(data_ + new_size, size_ - new_size); } size_ = new_size; } void PushBack(const T& value) { EmplaceBack(value); } void PushBack(T&& value) { EmplaceBack(std::move(value)); } void PopBack() noexcept { assert(size_ > 0); --size_; std::destroy_at(data_ + size_); } void Swap(Vector& other) noexcept { data_.Swap(other.data_); std::swap(size_, other.size_); } template <typename... U> T& EmplaceBack(U&&... value) { if (size_ == Capacity()) { // Требуется реаллокация памяти // Элемент value потенциально может располагаться в текущем Vector-е (внутри data_), // поэтому нельзя уничтожать data_ до завершения операции вставки RawMemory<T> new_data(size_ == 0 ? 1 : size_ * 2); // Сначала конструируем вставляемый элемент (может выбросить исключение) new (new_data + size_) T(std::forward<U>(value)...); try { // Переносим либо копируем оставшиеся элементы UninitializedMoveNIfNoexcept(data_.GetAddress(), size_, new_data.GetAddress()); } catch (...) { // Вставленный элемент нужно разрушить std::destroy_at(new_data + size_); throw; } std::destroy_n(data_.GetAddress(), size_); data_.Swap(new_data); } else { // Реаллокация не требуется. Просто конструируем новый элемент new (data_ + size_) T(std::forward<U>(value)...); } ++size_; return *data_.GetAddress(); } private: static T* UninitializedMoveNIfNoexcept(T* from, size_t n, T* to) { if constexpr (std::is_nothrow_move_constructible_v<T> || !std::is_copy_constructible_v<T>) { return std::uninitialized_move_n(from, n, to).second; } else { return std::uninitialized_copy_n(from, n, to); } } RawMemory<T> data_; size_t size_ = 0; };