目录
2,vector 的数据结构、一些简单功能的实现以及扩容逻辑
一,vector
vector 的数据安排以及操作方式,与 array 非常相似。两者的唯一差别在于空间的运用的灵活性。array 是静态空间,一旦配置了就不能改变,如果想要改变空间,那么得需要用户自己来:首先申请一块新空间,然后将元素从旧空间一一搬往新空间,再把原来的空间释放给系统。
vector 是动态空间,随着新元素的加入,它的内部机制会自动扩充空间以容纳新元素。因此,使用 vector 对于内存的合理利用与运用时的灵活性有很大的帮助,我们再也不必因为害怕空间不足而一开始就申请一个大空间的 array 了。
vector 的实现技术,关键在于其对大小的控制以及重新配置时的数据移动。当 vector 旧的空间已经装满元素时,如果用户每新增一个元素,vector 内部就只是扩充一个元素的空间,这样不是一个明智的做法,因为所谓扩充空间时需要 ①申请新空间 ②移动数据 ③释放旧空间,这是一个大工程,时间成本很高。一会我们就能知道 vector 是怎么在内部进行扩容的了。
关于 vector 的相关操作的使用方法,此文就不一一说明了,此文主要是简单的模拟实现。想要模拟实现一个vector,那就必须得对迭代器有一定的了解,如果不知道迭代器是什么,可以先看看这个:C++ 迭代器与反向迭代器-CSDN博客
1,vector 的迭代器
vector 维护的是一个连续线性空间,所以不论其元素类型是什么,普通指针都可以作为 vector 的迭代器而满足所有条件,因为vector迭代器所需要的操作行为,
如:operator*,operator->,operator++,operator--,operator+,operator-operator+=,operator-=,普通指针天生就具备。所以我们可以用原生指针来当 vector 的迭代器。
template<class T>
class vector {
// ...
public:
// 使用原生指针作为迭代器
typedef T* iterator;
typedef const T* const_iterator;
// ...
};
2,vector 的数据结构、一些简单功能的实现以及扩容逻辑
vector 所采用的数据结构非常简单:线性连续空间。它用两个迭代器 _start 和 _finish 分别指向申请得来的连续空间中目前已被使用的范围,并以迭代器 _end_of_storage 指向整块连续空间(含备用空间)的尾端:
template<class T>
class vector {
// ...
private:
// 一些成员变量
iterator _start = nullptr; // 当前使用的空间的首地址
iterator _finish = nullptr; // 当时使用空间的尾地址
iterator _end_of_storage = nullptr; // 当前可使用空间的尾地址
// ...
};
运用 _start、_finish、_end_of_storage 这三个迭代器可以轻松的实现迭代器的 begin() 与 end() 操作以及首尾访问、当前大小、当前容量、判断是否为空、清空、[]重载运算、swap等功能:
template<class T>
class vector {
// ...
public:
// 一些迭代器操作
iterator begin() { return _start; } /*正向迭代器*/
iterator end() { return _finish; }
const_iterator begin()const { return _start; }
const_iterator end()const { return _finish; }
typedef Reverse_Iterator<iterator> reverse_iterator; /*反向迭代器*/
typedef Reverse_Iterator<const_iterator> const_reverse_iterator;
reverse_iterator rbegin() { return end(); }
reverse_iterator rend() { return begin(); }
const_reverse_iterator rbegin()const { return end(); }
const_reverse_iterator rend()const { return begin(); }
public:
size_t size()const { return _finish - _start; } //有效元素的个数
size_t capacity()const { return _end_of_storage - _start; } //容量大小
bool empty()const { return size() == 0; } //容器是否为空
void clear() { _finish = _start; } //清空有效元素
//访问元素
T& back() { return *(end() - 1); }
const T& back()const { return *(end() - 1); }
T& front() { return *begin(); }
const T& front()const { return *begin(); }
T& operator[](size_t pos) {
assert(pos < size());
return *(_start + pos);
}
const T& operator[](size_t pos)const {
assert(pos < size());
return *(_start + pos);
}
// 将当前的 vector 与另一个 vector 交换
void swap(vector<T>& v) {
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
// ...
};
接下来是 vector 的扩容逻辑
为了降低空间配置时的速度成本,vector 实际配置的大小可能比用户的需求更大一些,以备将来可能的扩充。这便是容量(capacity)存在的意义。也就是说,一个 vector 的容量永远大于或等于其元素的个数。一旦元素的个数等于容量,当下次再有新增元素时,整个 vector 就得申请一块更大的新空间。如图:
扩容相关操作的实现:
template<class T>
class vector {
// ...
public:
// 扩容的相关操作
/*预留 n 个元素的空间*/
void reserve(size_t n) {
if (n > capacity()) {
iterator tmp = new T[n];
size_t sz = size();
for (size_t i = 0;i < sz;++i) {
tmp[i] = _start[i];
}
std::swap(tmp, _start);
_finish = _start + sz;
_end_of_storage = _start + n;
delete[] tmp;
}
}
/*将有效元素调节为 n 个*/
void resize(size_t n, const T& data = T()) {
reserve(n);
if (n > size()) {
for (size_t i = size();i < n;++i) {
_start[i] = data;
}
_finish = _start + n;
}
}
// ...
};
3,构造,拷贝与析构
template<class T>
class vector {
// ...
public:
//构造
vector() {}
vector(size_t n, const T& data = T()) { resize(n, data); }
vector(int n, const T& data = T()) { resize(n, data); }
template<class input_iterator> /*使用迭代器构造*/
vector(input_iterator begin, input_iterator end) {
while (begin != end) {
push_back(*(begin++));
}
}
//拷贝
vector(const vector<T>& v) {
_start = new T[v.capacity()];
for (size_t i = 0;i < v.size();++i) {
_start[i] = v[i];
}
_finish = _start + v.size();
_end_of_storage = _start + v.capacity();
}
vector<T>& operator=(vector<T> v) {
swap(v);
return *this;
}
//析构
~vector() {
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
// ...
};
4,插入与删除
STL 中 vector 所提供的相关操作有很多,这里就不一一实现了。
template<class T>
class vector {
// ...
public:
//增
// 在 vector 的尾部新添加一个元素
void push_back(const T& data) { insert(_finish, data); }
//在 pos 指向的位置之前插入元素
iterator insert(iterator pos, const T& data) {
assert(pos >= _start and pos <= _finish);
if (size() == capacity()) {
size_t tmp = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + tmp;
}
for (iterator it = _finish;it > pos;--it) {
*it = *(it - 1);
}
++_finish;
*pos = data;
return pos;
}
//删
// 删去 vector 头部的一个元素
void pop_back() { erase(_finish - 1); }
//删去 pos 指向的元素
iterator erase(iterator pos) {
assert(pos >= _start and pos < _finish);
for (iterator it = pos + 1;it < _finish;++it) {
*(it - 1) = *it;
}
--_finish;
return pos;
}
// ...
};
5,C++ 11的功能
这里主要实现的功能是 initializer_list 与 右值引用,如果对这两个东西不了解的话可以看看这两篇博客:
① initiallizer_list初始化
vector(const std::initializer_list<T>& list) {
for (const T& data : list) {
push_back(data);
}
}
支持了用initializer_list来构造,我们的vector就可以这样来初始化了:
vector<int> vec = {1,2,3,4,5};
② 右值引用相关
使用右值引用功能可以大大的提高我们代码的运行效率!
template<class T>
class vector {
// ...
public:
//移动构造与移动赋值
vector(vector&& v) :_start(v._start), _finish(v._finish), _end_of_storage(v._end_of_storage) {
v._start = v._finish = v._end_of_storage = nullptr;
}
vector<T>& operator=(vector&& v) {
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
return *this;
}
//插入
void push_back(T&& data) { insert(_finish, std::forward<T>(data)); }
iterator insert(iterator pos, T&& data) {
assert(pos >= _start and pos <= _finish);
if (size() == capacity()) {
size_t tmp = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + tmp;
}
for (iterator it = _finish;it > pos;--it) {
*it = *(it - 1);
}
++_finish;
*pos = std::forward<T>(data);
return pos;
}
// ...
};
5,完整代码:
namespace mySTL {
template<class T>
class vector {
public:
typedef T* iterator;
typedef const T* const_iterator;
private:
// 一些成员变量
iterator _start = nullptr; // 当前使用的空间的首地址
iterator _finish = nullptr; // 当时使用空间的尾地址
iterator _end_of_storage = nullptr; // 当前可使用空间的尾地址
public:
// 一些迭代器操作
iterator begin() { return _start; } /*正向迭代器*/
iterator end() { return _finish; }
const_iterator begin()const { return _start; }
const_iterator end()const { return _finish; }
typedef Reverse_Iterator<iterator> reverse_iterator; /*反向迭代器*/
typedef Reverse_Iterator<const_iterator> const_reverse_iterator;
reverse_iterator rbegin() { return end(); }
reverse_iterator rend() { return begin(); }
const_reverse_iterator rbegin()const { return end(); }
const_reverse_iterator rend()const { return begin(); }
public:
size_t size()const { return _finish - _start; } //有效元素的个数
size_t capacity()const { return _end_of_storage - _start; } //容量大小
bool empty()const { return size() == 0; } //容器是否为空
void clear() { _finish = _start; } //清空有效元素
//访问元素
T& back() { return *(end() - 1); }
const T& back()const { return *(end() - 1); }
T& front() { return *begin(); }
const T& front()const { return *begin(); }
T& operator[](size_t pos) {
assert(pos < size());
return *(_start + pos);
}
const T& operator[](size_t pos)const {
assert(pos < size());
return *(_start + pos);
}
// 将当前的 vector 与另一个 vector 交换
void swap(vector<T>& v) {
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
public:
//构造
vector(){}
vector(size_t n, const T& data = T()) { resize(n, data); }
vector(int n, const T& data = T()) { resize(n, data); }
template<class input_iterator> /*使用迭代器构造*/
vector(input_iterator begin, input_iterator end) {
while (begin != end) {
push_back(*(begin++));
}
}
//拷贝
vector(const vector<T>& v) {
_start = new T[v.capacity()];
for (size_t i = 0;i < v.size();++i) {
_start[i] = v[i];
}
_finish = _start + v.size();
_end_of_storage = _start + v.capacity();
}
vector<T>& operator=(vector<T> v) {
swap(v);
return *this;
}
//析构
~vector() {
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
public:
//扩容
void reserve(size_t n) { /*预留 n 个元素的空间*/
if (n > capacity()) {
iterator tmp = new T[n];
size_t sz = size();
for (size_t i = 0;i < sz;++i) {
tmp[i] = _start[i];
}
std::swap(tmp, _start);
_finish = _start + sz;
_end_of_storage = _start + n;
delete[] tmp;
}
}
void resize(size_t n, const T& data = T()) { /*将有效元素调节为 n 个*/
reserve(n);
if (n > size()) {
for (size_t i = size();i < n;++i) {
_start[i] = data;
}
_finish = _start + n;
}
}
public:
//增
// 在 vector 的尾部新添加一个元素
void push_back(const T& data) { insert(_finish, data); }
//在 pos 指向的位置之前插入元素
iterator insert(iterator pos, const T& data) {
assert(pos >= _start and pos <= _finish);
if (size() == capacity()) {
size_t tmp = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + tmp;
}
for (iterator it = _finish;it > pos;--it) {
*it = *(it - 1);
}
++_finish;
*pos = data;
return pos;
}
//删
// 删去 vector 头部的一个元素
void pop_back() { erase(_finish - 1); }
//删去 pos 指向的元素
iterator erase(iterator pos) {
assert(pos >= _start and pos < _finish);
for (iterator it = pos + 1;it < _finish;++it) {
*(it - 1) = *it;
}
--_finish;
return pos;
}
public:
// C++11
// initializer_list初始化
vector(const std::initializer_list<T>& list) {
for (const T& data : list) {
push_back(data);
}
}
//右值引用相关
//移动构造与移动赋值
vector(vector&& v) :_start(v._start), _finish(v._finish), _end_of_storage(v._end_of_storage) {
v._start = v._finish = v._end_of_storage = nullptr;
}
vector<T>& operator=(vector&& v) {
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
return *this;
}
//插入
void push_back(T&& data) { insert(_finish, std::forward<T>(data)); }
iterator insert(iterator pos, T&& data) {
cout << "yes";
assert(pos >= _start and pos <= _finish);
if (size() == capacity()) {
size_t tmp = pos - _start;
reserve(capacity() == 0 ? 4 : capacity() * 2);
pos = _start + tmp;
}
for (iterator it = _finish;it > pos;--it) {
*it = *(it - 1);
}
++_finish;
*pos = std::forward<T>(data);
return pos;
}
};
}
二,stack
stack 是一种先进后出(FirstInLastOut,FILO)的数据结构,它只有一个出口。stack 允许新增元素、移除元素、取得最顶端元素。但除了最顶端外,没有任何其它方法可以存取 stack 的其它元素。也就是说,stack 不能遍历。将元素推入 stack 的操作称为 push,将元素推出 stack 的操作称为 pop。
std::stack 是 STL(标准模板库)提供的容器适配器之一。容器适配器(Container Adapter)是 C++ STL(标准模板库)中的概念,它们是基于现有的容器提供的接口进行封装和扩展的类模板。容器适配器允许以不同的方式访问现有容器的元素,并提供了特定的操作和功能。简单来说,stack 就是靠封装其他容器来实现的。
stack 的底层实现会根据用户的选择来选择底层容器,我们可以选择 vector,也可以选择 deque 或者是 list。
namespace mySTL {
template<class T, class Container = vector<T>>
class stack {
private:
Container _cont; // 底层使用的容器
public:
void push(const T& data) { _cont.push_back(data); }
void push(T&& data) { _cont.push_back(std::forward<T>(data)); }
void pop() { _cont.pop_back(); }
T& top() { return _cont.back(); }
const T& top()const { return _cont.back(); }
size_t size() { return _cont.size(); }
bool empty() { return _cont.empty(); }
};
}
总之,vector 是一种动态数组,适用于需要高效的随机访问和在末尾插入/删除元素的场景。
stack 是一种后进先出的容器,适用于需要保持元素按照特定顺序访问的场景。
无论是 vector 还是 stack,它们都是 STL 提供的方便且易于使用的数据结构,能够简化和加速开发过程。