Untitled
unknown
plain_text
4 years ago
7.2 kB
21
Indexable
#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;
};Editor is loading...