#pragma once
#include <cassert>
#include <cstdlib>
#include <new>
#include <utility>
#include <memory>
#include <type_traits>
template <typename T>
class RawMemory {
public:
RawMemory() = default;
RawMemory(const RawMemory&) = delete;
RawMemory& operator=(const RawMemory& rhs) = delete;
RawMemory(RawMemory&& other) noexcept
: buffer_(other.buffer_)
, capacity_(other.capacity_)
{
other.buffer_ = nullptr;
other.capacity_ = 0;
}
RawMemory& operator=(RawMemory&& rhs) noexcept
{
if (this != &rhs) {
Deallocate(buffer_);
buffer_ = rhs.buffer_;
capacity_ = rhs.capacity_;
rhs.buffer_ = nullptr;
rhs.capacity_ = 0;
}
return *this;
}
explicit RawMemory(size_t capacity)
: buffer_(Allocate(capacity))
, capacity_(capacity) {
}
~RawMemory() {
Deallocate(buffer_);
}
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;
Vector(const Vector& other)
: data_(other.size_)
, size_(other.size_)
{
std::uninitialized_copy_n(other.data_.GetAddress(), other.size_, data_.GetAddress());
}
Vector& operator=(const Vector& rhs)
{
if (this != &rhs) {
if (rhs.size_ > data_.Capacity()) {
Vector rhs_copy(rhs);
Swap(rhs_copy);
}
else {
if (rhs.size_ < size_) {
for (size_t i = 0; i != rhs.size_; ++i) {
data_[i] = rhs.data_[i];
}
std::destroy_n(data_.GetAddress(), size_ - rhs.size_);
}
else {
for (size_t i = 0; i != size_; ++i) {
data_[i] = rhs.data_[i];
}
std::uninitialized_copy_n(rhs.data_.GetAddress(), rhs.size_ - size_, data_.GetAddress());
}
size_ = rhs.size_;
}
}
return *this;
}
Vector(Vector&& other) noexcept
{
Swap(other);
other.size_ = 0;
}
Vector& operator=(Vector&& rhs) noexcept
{
if (this != &rhs) {
Swap(rhs);
rhs.size_ = 0;
}
return *this;
}
void Swap(Vector& other) noexcept
{
data_.Swap(other.data_);
std::swap(size_, other.size_);
}
explicit Vector(size_t size)
: data_(size)
, size_(size) //
{
std::uninitialized_value_construct_n(data_.GetAddress(), size);
}
~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);
// constexpr оператор if будет вычислен во время компиляции
if constexpr (std::is_nothrow_move_constructible_v<T> or not 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 (size_ < new_size) {
if (Capacity() < new_size) {
Reserve(new_size);
}
std::uninitialized_value_construct_n(data_.GetAddress(), new_size - size_);
}
else if (size_ > new_size) {
std::destroy_n(data_.GetAddress(), size_ - new_size);
}
else return;
size_ = new_size;
}
void PushBack(const T& value)
{
if (size_ == Capacity()) {
size_t size_new = 0;
if (size_ == 0) {
size_new = 1;
}
else {
size_new = size_ * 2;
}
RawMemory<T> new_data(size_new);
new (new_data + size_) T(value);
std::uninitialized_move_n(data_.GetAddress(), size_, new_data.GetAddress());
std::destroy_n(data_.GetAddress(), size_);
data_.Swap(new_data);
}
else {
new (data_ + size_) T(value);
}
++size_;
}
void PushBack(T&& value)
{
if (size_ == Capacity()) {
size_t size_new = 0;
if (size_ == 0) {
size_new = 1;
}
else {
size_new = size_ * 2;
}
RawMemory<T> new_data(size_new), t(1);
t[0] = value;
new (new_data + size_) T(std::move(t[0]));
std::uninitialized_move_n(data_.GetAddress(), size_, new_data.GetAddress());
std::destroy_n(data_.GetAddress(), size_);
data_.Swap(new_data);
}
else {
new (data_ + size_) T(std::move(value));
}
++size_;
}
void PopBack()
{
if (size_) {
std::destroy_n(data_.GetAddress(), 1);
--size_;
}
else return;
}
private:
RawMemory<T> data_;
size_t size_ = 0;
};