Untitled

mail@pastecode.io avatar
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;
};