C++簡單實現vector
阿新 • • 發佈:2022-03-07
向量
向量是序列容器,表示可以更改大小的陣列。
就像陣列一樣,向量對其元素使用連續的儲存位置,這意味著也可以使用指向其元素的常規指標上的偏移量來訪問其元素,並且與陣列一樣高效。但與陣列不同的是,它們的大小可以動態變化,它們的儲存由容器自動處理。
在內部,向量使用動態分配的陣列來儲存其元素。可能需要重新分配此陣列,以便在插入新元素時增加大小,這意味著分配新陣列並將所有元素移動到該陣列。就處理時間而言,這是一項相對昂貴的任務,因此,每次將元素新增到容器時,向量都不會重新分配。
相反,向量容器可以分配一些額外的儲存以適應可能的增長,因此容器的實際容量可能大於嚴格需要的儲存來包含其元素(即其大小)。庫可以實現不同的增長策略,以平衡記憶體使用和重新分配,但無論如何,重新分配應僅以對數增長的大小間隔發生,以便可以在向量末尾插入單個元素,並提供攤銷的恆定時間複雜性。
因此,與陣列相比,向量消耗更多的記憶體,以換取管理儲存和以有效方式動態增長的能力。
與其他動態序列容器(deques、list 和 forward_lists)相比,向量非常有效地訪問其元素(就像陣列一樣),並且相對有效地從其末尾新增或刪除元素。對於涉及在末尾以外的位置插入或刪除元素的操作,它們的效能比其他元素差,並且迭代器和引用的一致性低於 lists 和 forward_lists。
cpp
template <typename T> class Vector { public: Vector() noexcept = default; explicit Vector(size_t n) : cap_{n}, ptr_{alloc(cap_)} { for (; len_ < n; ++len_) { construct(ptr_ + len_); //呼叫T的預設構造 } } Vector(size_t n, const T &x) : cap_{n}, ptr_{alloc(cap_)} { for (; len_ < n; ++len_) { construct(ptr_ + len_, x); //呼叫T的拷貝構造 } } Vector(const Vector &x) : cap_{x.size()}, ptr_{alloc(cap_)} //拷貝構造 { for (; len_ < x.size(); ++len_) { construct(ptr_ + len_, x[len_]); } } Vector(Vector &&x) noexcept //移動構造 { cap_ = std::__exchange(x.cap_, 0); len_ = std::__exchange(x.len_, 0); ptr_ = std::__exchange(x.ptr_, nullptr); } Vector(std::initializer_list<T> li) : cap_{li.size()}, ptr_{alloc(cap_)} //初始化列表 { for (auto &x : li) { construct(ptr_ + len_, x); ++len_; } } ~Vector() noexcept { clear(); dealloc(ptr_); } void swap(Vector &x) noexcept { using std::swap; // ADL swap(cap_, x.cap_); swap(len_, x.len_); swap(ptr_, x.ptr_); } void clear() noexcept { for (; len_ > 0; --len_) { destroy(ptr_ + len_ - 1); } } Vector &operator=(const T &x) //拷貝賦值 { if (this != &x) { Vector{x}.swap(*this); } return *this; } Vector &operator=(T &&x) noexcept //移動賦值 { if (this != &x) { Vector{std::move(x)}.swap(*this); } return *this; } Vector &operator=(std::initializer_list<T> li) //初始化列表賦值 { Vector{li}.swap(*this); return *this; } void push_back(const T &x) //拷貝 { emplace_back(x); } void push_back(T &&x) //移動 { emplace_back(x); } template <typename... Args> void emplace_back(Args &&...args) //直接傳遞建構函式 { if (len_ == cap_) { size_t new_cap = cap_ ? cap_ * 2 : 1; //等0返回1 T *new_ptr = alloc(new_cap); for (size_t new_len; new_len < len_; ++new_len) { construct(new_ptr + new_len, std::move_if_noexcept(ptr_[new_len])); } cap_ = new_cap; ptr_ = new_ptr; } construct(ptr_ + len_, std::forward<Args>(args)...); ++len_; } void pop_back() noexcept { if (len_ < cap_ / 2) { size_t new_cap = cap_ / 2; T *new_ptr = alloc(new_cap); for (size_t new_len = 0; new_len < len_; ++new_len) { construct(new_ptr + new_len, std::move_if_noexcept(ptr_[new_len])); } cap_ = new_cap; ptr_ = new_ptr; } destroy(ptr_ + len_ - 1); --len_; } size_t size() const noexcept { return len_; } size_t capacity() const noexcept { return cap_; } bool empty() const noexcept { return len_ == 0; } T &operator[](size_t i) { return ptr_[i]; } const T &operator[](size_t i) const { return ptr_[i]; } T *begin() noexcept { return ptr_; } T *end() noexcept { return ptr_ + len_; } const T *begin() const noexcept { return ptr_; } const T *end() const noexcept { return ptr_ + len_; } private: T *alloc(size_t n) //分配n個大小記憶體 { return static_cast<T *>(::operator new(sizeof(T) * n)); } void dealloc(T *p) noexcept //釋放記憶體 { ::operator delete(p); } template <typename... Args> void construct(T *p, Args &&...args) //在這塊記憶體上構造T型別物件 { ::new (p) T(std::forward<Args>(args)...); } void destroy(T *p) noexcept { p->~T(); } private: size_t cap_{0}; //容量 size_t len_{0}; //元素個數 T *ptr_{nullptr}; };