⭐本篇重点:vector深入理解和模拟实现
⭐本篇代码:c++学习/09.vector-2 · 橘子真甜/c++-learning-of-yzc - 码云 - 开源中国 (gitee.com)
目录
一. 深入理解vector
二. 使用模板模拟实现vector (包含迭代器)
2.1 模拟vector类的成员
2.1 size capacity
2.3 构造函数与拷贝构造函数
2.4 析构函数
2.5 reverse与resize
2.6 Insert与push_back
2.7 测试代码1
编辑 2.8 erase和pop_back
2.9 测试代码2
2.10 迭代器begin和end
2.11 测试代码3
三. 测试类类型的变量
四. vector完成动态二维数组
一. 深入理解vector
上篇vector我们提到,当vector插入元素之后,如果空间不足就会扩容1.5倍或者2倍。如果扩容之后空间仍然不够会再次扩容。
并且vector的扩容不是在原空间上直接扩容,而是新开辟一份空间,拷贝原空间的数据到新空间,放入新元素,释放空间等过程。
所以我们使用vector之前,要先预算并开辟要使用的空间。避免频繁扩容导致效率降低。最好不要频繁的使用push_back
二. 使用模板模拟实现vector (包含迭代器)
源文件 test.cpp用于测试
头文件 myVector.h用于声明和定义vector类
2.1 模拟vector类的成员
根据我们使用的vector,可以知道vector至少有这些成员变量
#pragma once
#include <iostream>
using namespace std;
namespace YZC
{
template<class T>
class myVector
{
public:
private:
T* _begin; //数组开始
T* _end; //数组结尾
T* _endOfStorage; //容量结尾
};
}
_begin表示这个容器开始的位置,_end表示结尾位置。_endOfStorage表示容量结尾的位置
为了支持迭代器,我们可以进一步封装
#pragma once
#include <iostream>
using namespace std;
namespace YZC
{
template<class T>
class myVector
{
//添加迭代器
typedef T* iterator;
typedef const T* const_iterator;
public:
private:
iterator _begin; //数组开始
iterator _end; //数组结尾
iterator _endOfStorage; //容量结尾
};
}
而成员函数包括:
构造函数,析构函数,拷贝构造,赋值运算符重载,扩容reverse,设置元素resize,插入元素push_back,insert,删除元素pop_back,erase等...
以及 size,capacity,begin,end,[]重载,等访问修改元素,容量,迭代器等函数
2.1 size capacity
这两个函数比较简单,直接通过迭代器相减即可获得
//获取容量
size_t capacity()
{
return _endOfStorage - _begin;
}
//[]重载
T& operator[](size_t i)
{
//注意要防止数组越界
assert(i < size());
return _begin[i];
}
//[]重载
const T& operator[](size_t i)const
{
assert(i < size());
return _begin[i];
}
2.3 构造函数与拷贝构造函数
主要拷贝构造函数需要进行深拷贝
#pragma once
#include <iostream>
using namespace std;
namespace YZC
{
template<class T>
class myVector
{
//添加迭代器
typedef T* iterator;
typedef const T* const_iterator;
public:
//构造函数
myVector()
:_begin(nullptr)
, _end(nullptr)
, _endOfStorage(nullptr)
{};
//拷贝构造
myVector(const myVector& v)
{
//需要深拷贝
//依次拷贝成员变量,然后赋值
_begin = new T[v.capacity()];
_end = _begin; //end在拷贝元素之后计算获取
_endOfStorage = _begin + v.capacity();
for (size_t i = 0; i < v.size(); i++)
{
*_end = v[i];
_end++;
}
}
//获取元素数量
size_t size()
{
return _end - _begin;
}
//获取容量
size_t capacity()
{
return _endOfStorage - _begin;
}
//[]重载
T& operator[](size_t i)
{
//注意要防止数组越界
assert(i < size());
return _begin[i];
}
//[]重载
const T& operator[](size_t i)const
{
assert(i < size());
return _begin[i];
}
private:
iterator _begin; //数组开始
iterator _end; //数组结尾
iterator _endOfStorage; //容量结尾
};
}
2.4 析构函数
~myVector()
{
delete[] _begin;
_begin = _end = _endOfStorage = nullptr;
}
2.5 reverse与resize
注意好扩容的机制即可,开辟新空间,转移元素,销毁旧空间。
注意拷贝数据的时候,要深拷贝,不能简单的使用memcpy进行浅拷贝
void reserve(size_t n)
{
//防止非法扩容
assert(n >= 0);
//开始扩容
if (n > capacity())
{
//开辟新空间
size_t oldsize = size(); //旧空间元素的大小
T* newbegin = new T[n];
if (_begin != nullptr)
{
//拷贝旧元素
//不可以使用memcpy,因为这是浅拷贝
for (size_t i = 0; i < oldsize; i++)
{
newbegin[i] = _begin[i]; //对于类类型,调用其operator= 是深拷贝
}
//删除旧空间
delete[] _begin;
_begin = nullptr;
}
_begin = newbegin;
_end = _begin + oldsize;
_endOfStorage = _begin + n;
}
}
void resize(size_t n, const T& val = T()) //T()表示这种类型的默认值,比如int()=0
{
if (n <= size())
{
_end = _begin + n;
}
else
{
//如果n大于容量,调用reverse进行扩容
if (n > capacity())
{
reserve(n);
}
//赋值val
while (_end < _begin+ n)
{
*_end = val;
_end++;
}
}
}
2.6 Insert与push_back
对于push_back我们可以复用Insert的代码,直接在结尾插入。所以我们主要实现Insert这个函数的代码。
Insert
//在pos位置插入元素x
void Insert(iterator pos, const T& x)
{
//1. 需要合法插入
assert(pos <= _end); //当pos和end相等的时候就是尾插push_back
//2. 判断是否需要扩容
if (_end == _endOfStorage)
{
//扩容导致迭代器失效,需要提前计算好pos的位置
size_t newpos = pos - _begin;
size_t newcapacity = capacity() == 0 ? 3 : capacity() * 2;
reserve(newcapacity);
pos = _begin + newpos; //重新设置pos
}
//插入元素
iterator end = _end - 1;
while (end >= pos) //不可end != pos 假如只有一个数据就会崩溃
{
*(end + 1) = *end;
--end;
}
*pos = x;
_end++;
}
push_back
复用Insert的代码
//尾插
void push_back(const T& x)
{
Insert(_end, x);
}
2.7 测试代码1
我们的代码已经基本完善了,可以进行一次测试验证是否有bug
头文件 myVector.h 如下
#pragma once
#include <iostream>
#include <cassert>
using namespace std;
namespace YZC
{
template<class T>
class myVector
{
//添加迭代器
typedef T* iterator;
typedef const T* const_iterator;
public:
//构造函数
myVector()
:_begin(nullptr)
, _end(nullptr)
, _endOfStorage(nullptr)
{};
//拷贝构造
myVector(const myVector& v)
{
//需要深拷贝
//依次拷贝成员变量,然后赋值
_begin = new T[v.capacity()];
_end = _begin; //end在拷贝元素之后计算获取
_endOfStorage = _begin + v.capacity();
for (size_t i = 0; i < v.size(); i++)
{
*_end = v[i];
_end++;
}
}
~myVector()
{
delete[] _begin;
_begin = _end = _endOfStorage = nullptr;
}
void reserve(size_t n)
{
//防止非法扩容
assert(n >= 0);
//开始扩容
if (n > capacity())
{
//开辟新空间
size_t oldsize = size(); //旧空间元素的大小
T* newbegin = new T[n];
if (_begin != nullptr)
{
//拷贝旧元素
//不可以使用memcpy,因为这是浅拷贝
for (size_t i = 0; i < oldsize; i++)
{
newbegin[i] = _begin[i]; //对于类类型,调用其operator= 是深拷贝
}
//删除旧空间
delete[] _begin;
_begin = nullptr;
}
_begin = newbegin;
_end = _begin + oldsize;
_endOfStorage = _begin + n;
}
}
void resize(size_t n, const T& val = T()) //T()表示这种类型的默认值,比如int()=0
{
if (n <= size())
{
_end = _begin + n;
}
else
{
//如果n大于容量,调用reverse进行扩容
if (n > capacity())
{
reserve(n);
}
//赋值val
while (_end < _begin+ n)
{
*_end = val;
_end++;
}
}
}
//在pos位置插入元素x
void Insert(iterator pos, const T& x)
{
//1. 需要合法插入
assert(pos <= _end); //当pos和end相等的时候就是尾插push_back
//2. 判断是否需要扩容
if (_end == _endOfStorage)
{
//扩容导致迭代器失效,需要提前计算好pos的位置
size_t newpos = pos - _begin;
size_t newcapacity = capacity() == 0 ? 3 : capacity() * 2;
reserve(newcapacity);
pos = _begin + newpos; //重新设置pos
}
//插入元素
iterator end = _end - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
_end++;
}
//尾插
void push_back(const T& x)
{
Insert(_end, x);
}
//获取元素数量
size_t size()
{
return _end - _begin;
}
//获取容量
size_t capacity()
{
return _endOfStorage - _begin;
}
//[]重载
T& operator[](size_t i)
{
//注意要防止数组越界
assert(i < size());
return _begin[i];
}
//[]重载
const T& operator[](size_t i)const
{
assert(i < size());
return _begin[i];
}
private:
iterator _begin; //数组开始
iterator _end; //数组结尾
iterator _endOfStorage; //容量结尾
};
}
源文件 test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "myVector.h"
void test1()
{
YZC::myVector<int> arr;
//插入20个随机值
for (int i = 0; i < 20; i++)
{
arr.push_back(rand());
}
//遍历输出
for (int i = 0; i < arr.size(); i++)
{
cout << arr[i] << " ";
}
cout << endl;
//使用[]修改
for (int i = 0; i < 20; i++)
{
arr[i] = i;
}
for (int i = 0; i < arr.size(); i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
int main()
{
//设置随机种子
srand((unsigned int)time(0));
test1();
return 0;
}
运行结果如下:
2.8 erase和pop_back
erase将pos后面的元素一个一个向前移动即可,最后让_end减去1
而pop_back只要让_end减去1即可
iterator& Erase(iterator pos)
{
assert(pos < _end);
iterator it = pos;
while (it != _end)
{
*it = *(it + 1);
it++;
}
_end--;
return pos;
}
void pop_back()
{
assert(_begin < _end);
--_end;
}
2.9 测试代码2
#define _CRT_SECURE_NO_WARNINGS 1
#include "myVector.h"
void test1()
{
YZC::myVector<int> arr;
for (int i = 0; i < 20; i++)
{
arr.push_back(i);
}
arr.pop_back();
arr.pop_back();
arr.pop_back();
arr.push_back(1234);
for (int i = 0; i < arr.size(); i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
int main()
{
test1();
return 0;
}
2.10 迭代器begin和end
//开始位置的迭代器
iterator begin()const
{
return _begin;
}
//结束位置后一个的迭代器
iterator end()const
{
return _end;
}
有了begin和end后,我们就可以使用迭代器遍历和C++11 for循环
2.11 测试代码3
使用迭代器注意要将iterator设置为public
测试代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include "myVector.h"
#include <algorithm>
void test1()
{
YZC::myVector<int> arr;
//插入20个随机值
for (int i = 0; i < 20; i++)
{
arr.push_back(rand() % 1000);
}
//1. 迭代器遍历
YZC::myVector<int>::iterator it = arr.begin();
while (it != arr.end())
{
cout << *it << " ";
it++;
}
cout << endl;
//使用sort和迭代器进行排序
sort(arr.begin(), arr.end());
//2. C++11for循环
for (const auto& e : arr)
cout << e << " ";
cout << endl;
}
int main()
{
//设置随机种子
srand((unsigned int)time(0));
test1();
return 0;
}
运行结果如下:
三. 测试类类型的变量
上面的代码,经过测试,基本数据类型是没什么问题的。那么对于自定义类型或者系统自带的类型有没有错误呢?
我们以string类为测试
#define _CRT_SECURE_NO_WARNINGS 1
#include "myVector.h"
#include <string>
//测试string类型
void test2()
{
YZC::myVector<string> arr;
//插入元素
string s1 = "YZC";
string s2 = "123";
string s3 = "vector";
string s4 = "string";
string s5 = "abc";
string s6 = "231141234";
arr.push_back(s1);
arr.push_back(s2);
arr.push_back(s3);
arr.push_back(s4);
arr.push_back(s5);
arr.push_back(s6);
for (const auto& e : arr)
cout << e << " ";
cout << endl;
//删除和修改元素
arr[0] = "yzc";
arr.pop_back();
for (const auto& e : arr)
cout << e << " ";
cout << endl;
}
int main()
{
test2();
return 0;
}
测试结果如下:
注意:如果是自定义类型,必须要定义operator=之后才能插入数据,否则会报错!
四. vector完成动态二维数组
vector可以完成一个动态一维数组,那么如果使用vector定义一个二维数组?其实很简单,只要在将一个vector作为vector的元素不就可以了?
举例如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include "myVector.h"
#include <vector>
void test1()
{
//vector中的元素是vector<int> 可以表示一个int的二维数组
vector<vector<int>> arr;
int index = 1;
for (int i = 0; i < 6; i++)
{
vector<int> tmp;
for (int j = 0; j < 6; j++)
{
tmp.push_back(index++);
}
arr.push_back(tmp);
}
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 6; j++)
{
cout << arr[i][j] << " ";
}
cout << endl;
}
}
int main()
{
test1();
return 0;
}
运行结果: