上一次我们认识了vector的各种接口, 我们可以发现, STL容器中的许多接口都是非常类似的, 使用方法也很相似, 方便了我们的使用. 那么今天, 我们趁热打铁, 来模拟实现一下vector, 加深对vector容器的理解. 耐心看完, 你一定有所收获~
文章目录
一. vector的结构描述
vector实际上是由三个指针描述的, 如下如所示 :
所以vector的成员就是三个指针
而且vector应用模板, 实现泛型
template <class T>
class Vector {
private:
T* _start; //start: 首元素地址
T* _finish; //finish: 最后一个元素的下一个位置
T* _endofstorage; //endofstorage: 整个空间的尾地址
}
二. 基本功能
1. 构造函数
//构造函数
Vector()
: _start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{}
这里给出的是一个无参的默认构造, 给三个指针都赋值为nullptr
这里也可以实现其他的构造函数, 比如用n个数据val构造, 开好空间之后进行循环尾插即可
2. 拷贝构造
//拷贝构造
Vector(const Vector& v)
: _start(nullptr)
, _finish(nullptr)
, _endofstorage(nullptr)
{
//开好空间
reserve(v.capacity());
iterator it = begin();
const_iterator vit = v.begin();
//遍历赋值
while (vit != v.end()) {
*it = *vit;
it++;
vit++;
}
//修改_finish指针
_finish += v.size();
}
思路:
1. 先开好空间, 避免之后多次新开空间
2. 拿到当前对象*this 和 要赋值对象v 的迭代器
3. 循环赋值(深拷贝)
4. 修改_finish指针 (实际上就是修改有效元素个数 size)
3. 赋值运算符
//交换函数
void Swap(Vector<T> v) {
swap(_start, v._start);
swap(_finish, v._finish);
swap(_endofstorage, v._endofstorage);
}
//赋值运算符
Vector<T>& operator=(Vector<T> v) {
Swap(v);
return *this;
}
赋值运算符的现代写法
自定义一个交换函数, 把成员全部交换给创建好的临时变量即可
4. 析构函数
//析构
~Vector() {
if (_start) {
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
}
析构函数: 释放资源, 也就是把这个数组的空间释放即可
三. 迭代器
首先来看迭代器的声明:
//Vector迭代器: T*
typedef T* iterator;
typedef const T* const_iterator;
1. begin 和 end
iterator begin() {
return _start;
}
iterator end() {
return _finish;
}
const_iterator begin() const {
return _start;
}
const_iterator end() const {
return _finish;
}
2. operator[ ]
//operator[] 获取pos位置元素
T& operator[](size_t pos) {
assert(pos < size());
return _start[pos];
}
// [] const接口
const T& operator[](size_t pos) const {
assert(pos < size());
return _start[pos];
}
四. 容量相关操作
1. size
//获取size
size_t size() const {
return _finish - _start;
}
在刚开始的时候那个图还记得吗?... _finish - _start 即可
2. capacity
//获取容量capacity
size_t capacity() const {
return _endofstorage - _start;
}
还是那副图, 指针相减即可
3. reserve增容 (memcpy拷贝的问题 *)
//增容
void reserve(size_t n) {
if (n > capacity()) {
//保存size
size_t sz = size();
//1. 开空间
T* tmp = new T[n];
//2. 内容拷贝
//memcpy为内存拷贝, 是浅拷贝, 遇到有资源的类型会发生问题
//memcpy(tmp, _start, sizeof(T) * size());
//执行T类型的赋值操作, 进行深拷贝
for (int i = 0; i < sz; ++i) {
tmp[i] = _start[i];
}
//3. 释放原有空间
delete[] _start;
//4. 更新成员变量
_start = tmp;
_endofstorage = _start + n;
_finish = _start + sz;
}
}
相信看过之前实现的应该明白这都是老步骤了
开空间, 拷贝内容, 释放原有空间, 更新指针指向 和 size capacity
最后更新capacity的时候: _start + n即可
而更新size 的时候: _start + 有效元素的个数, 这里不知道, 所以要在开头保存一下原有的size
接下来说一下memcpy的拷贝问题
我们知道, memcpy是字节拷贝(内存拷贝), 而根据经验, 字节拷贝都是浅拷贝, 在涉及资源时是行不通的. 下面我们就来看看是怎么回事
这里不好直接描述, 下面直接给出图示:
4. resize
//设置size
void resize(size_t n, const T& val = T()) {
//增容
if (n > capacity()) {
reserve(n);
}
//给新的位置赋值 [size, n)
if (n > size()) {
iterator it = end();
while (it != _start + n) {
*it = val;
++it;
}
}
//修改size
_finish = _start + n;
}
resize(n, ch)
1. n <= size: 只需要修改size
2. size < n <= capacity: 赋值ch --> [size, capacity) , 修改size
3. n > capacity: 增容, 赋值, 修改size
五. 插入和删除
1. insert
//插入 (在pos位置前插入一个元素)
void insert(iterator pos, const T& val) {
assert(pos >= begin() && pos <= end());
//获取pos 和 begin的偏移量
int offset = pos - _start;
//检查容量
if (_finish == _endofstorage) {
size_t newCap = (_start == nullptr) ? 1 : 2 * capacity();
reserve(newCap);
}
//增容后, 原有空间被释放, pos迭代器失效, 需要更新pos的位置
pos = _start + offset;
//元素移动 --- 从后向前
iterator it = end();
while (it != pos) {
*it = *(it - 1);
--it;
}
//插入
*pos = val;
//更新size
++_finish;
}
还是熟悉的步骤:
- 检查容量, 是否需要增容
- 元素移动 — 从后往前
- 插入, 更新size
vector中要注意增容后迭代器失效的问题, 所以开始要获取与start的偏移量
2. erase
//删除 (删除pos位置的元素)
iterator erase(iterator pos) {
assert(pos >= begin() && pos < end());
//移动元素 --- 从前向后
iterator it = pos + 1;
while (it != end()) {
*(it - 1) = *it;
//*it = *(it + 1); 错误的, 因为end()位置不是有效位置, 没有元素
++it;
}
//更新size
--_finish;
//返回被删除元素的下一个位置, 即pos的位置 (被删除的下一个位置移到pos这了)
return pos;
}
- 移动元素 ---- 从前向后
- 更新size
这里不再实现头插, 尾插, 头删, 尾删等等接口, 因为可以直接给出位置复用以上两个代码即可
六. 测试
构造, 拷贝, = , 3种遍历的测试
void test() {
Vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
Vector<int> v2(v);
v2.pop_back();
Vector<int> v3;
v3 = v2;
v3.pop_back();
//[]遍历
cout << "operator[]: " << endl;
for (size_t i = 0; i < v.size(); i++) {
cout << v[i] << " ";
//可以修改
v[i] = 10;
}
cout << endl;
for (size_t i = 0; i < v.size(); i++) {
cout << v[i] << " ";
}
cout << endl;
//迭代器
cout << "iterator: " << endl;
Vector<int>::iterator it = v.begin();
while (it != v.end()) {
cout << *it << " ";
*it = 5;
it++;
}
cout << endl;
it = v.begin();
while (it != v.end()) {
cout << *it << " ";
it++;
}
cout << endl;
//范围for
cout << "范围for: " << endl;
for (auto& e : v) {
cout << e << " ";
e = 7;
}
cout << endl;
for (const auto& e : v) {
cout << e << " ";
}
cout << endl;
}
运行结果如下:
插入, 删除 , resize的测试
template <class T>
void printV(const Vector<T>& v) {
for (const auto& e : v) {
cout << e << " ";
}
cout << endl;
}
void test2() {
Vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);
printV(v);
v.resize(9);
printV(v);
v.resize(4, 20);
printV(v);
v.resize(6, 30);
printV(v);
}
运行结果如下:
void test3() {
Vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.insert(v.end(), 4);
v.insert(v.begin(), 0);
printV(v);
}
运行结果如下 :
删除重复的元素
void test4() {
Vector<int> v;
v.push_back(1);
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(1);
v.push_back(1);
v.push_back(1);
printV(v);
//删除所有的1
Vector<int>::iterator it = v.begin();
while (it != v.end()) {
//删除之后, 要从新获取erase接口的返回值, 让it更新到被删除元素的下一个位置
if (*it == 1)
it = v.erase(it);
else
++it;
}
printV(v);
}
运行结果如下 :
vector中套string类型测试
void test5() {
Vector<string> v;
v.push_back("123");
v.push_back("456");
v.push_back("78");
v.push_back("9");
printV(v);
}
运行结果如下 :
OK , 到这里就结束了