目录
1.push_back / pop_back / insert / erase 的使用
3. vector 插入数据是如何实现的,为什么增容增加 2倍,1.5倍不行吗?
vector的概念
上一节,我们学习了string的使用及模拟实现。
String 是把单向信息存起来,比如身份证号码,而vector是把多项相同的信息存起来,比如每个人的姓名、年龄、分数 保存到 vector 容器中。
我们可以把 vector看成顺序表,只不过在顺序表的基础上增加了一些功能,提供了丰富的成员函数和操作符重载,如迭代器支持、各种算法的适配等。而顺序表比较单一。
string 和 vector<char> 有什么区别???
- vector 是一个动态的数组,里面存指定的类型,不够都可以动态增长
- string 也具有类似的动态特性,能够自动管理内存以适应字符串内容的变化
- string 有 \0,vector 没有 \0
- string 支持+=、输入操作
- vector存其它类型+=,输入的意义就不大。
vector的使用
和string的使用类似,如果遇到不会的可以查阅文档。
1.push_back / pop_back / insert / erase 的使用
push_back尾插
#include <iostream>
using namespace std;
#include <vector>
void test_vector1()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
for (auto e : v)
{
cout << e << " "; // 1 2 3 4
}
cout << endl;
}
int main()
{
test_vector1();
return 0;
}
pop_back尾删
#include <iostream>
using namespace std;
#include <vector>
void test_vector1()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
//尾删
v.pop_back();
v.pop_back();
for (auto e : v)
{
cout << e << " "; // 1 2
}
cout << endl;
}
int main()
{
test_vector1();
return 0;
}
insert任意位置的插入
insert 需要配合迭代器使用
#include <iostream>
using namespace std;
#include <vector>
void test_vector1()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.insert(v.begin(), 0); //头插 0
v.insert(v.begin(), -1); //再头插 -1
v.insert(v.begin() + 1, 10);// 再插入完 -1 之后,再 -1的后面插入 10
for (auto e : v)
{
cout << e << " "; // -1 10 0 1 2 3 4
}
cout << endl;
}
int main()
{
test_vector1();
return 0;
}
erase任意位置的删除
同样配合迭代器使用 v.erase( begin() + 1), 加几就是删掉下标为几的值
#include <iostream>
using namespace std;
#include <vector>
void test_vector1()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
//任意位置的删除
v.erase(v.begin()); // 删除第一个位置,变成 2 3 4
v.erase(v.begin() + 1); //2 3 4 中,删除 3 这个元元素
for (auto e : v)
{
cout << e << " "; // 2 4
}
cout << endl;
}
int main()
{
test_vector1();
return 0;
}
假设我要删除的数为指定的值的时候该怎么删除呢?
使用迭代器删除的时候相对于指定该数的下标,问题是我们不知道这个数的位置,如何删除呢?vector 中没有提供 find 查找功能
进入手册,在 C++ 的标准算法库<algorithm>
中有 find
算法可以用于在容器中查找特定元素。(记得包文件)
#include <iostream>
using namespace std;
#include <vector>
#include <algorithm>
void test_vector1()
{
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(5);
v.push_back(30);
v.push_back(40);
vector<int>::iterator pos = find(v.begin(), v.end(), 5); //find用的算法里面的
if (pos != v.end())
{
v.erase(pos); // erase中依旧填 迭代器
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
test_vector1();
return 0;
}
2.构造函数和拷贝构造
#include <iostream>
using namespace std;
#include <vector>
void test_vector2()
{
vector<int> v1; //构造,初始化 v1
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
vector<int> v2(v1); //拷贝构造 即初始化 v2
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
for (size_t i = 0; i < v2.size(); ++i)
{
cout << v2[i] << " ";
}
cout << endl;
}
int main()
{
test_vector2();
return 0;
}
3.赋值运算符 operator=
using namespace std;
#include <vector>
void test_vector3()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
vector<int> v3;
v3.push_back(10);
v3.push_back(20);
v3.push_back(30);
v3.push_back(40);
v1 = v3; //赋值
for (size_t i = 0; i < v1.size(); ++i)
{
cout << v1[i] << " ";
}
cout << endl;
}
int main()
{
test_vector3();
return 0;
}
4.遍历数据
#include <iostream>
using namespace std;
#include <vector>
void test_vector4()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
//1.遍历数据 修改数据
//operator[] + size
for (size_t i = 0; i < v.size(); ++i)
{
v[i] *= 2; //修改数据,都可以修改
cout << v[i] << " ";
}
cout << endl;
//2迭代器
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//3.范围for -> 被编译器替换成迭代器方式遍历支持的
for (auto e : v) // 加上引用后,修改会影响原容器中的元素
{
//e *= 2; //不加引用修改不会影响原来的容器
cout << e << " ";
}
cout << endl;
}
int main()
{
test_vector4();
return 0;
}
5.迭代器
迭代器有四种:非const 正向、非const 反向、const 正向、const 反向
const 对象通常在传参的时候才会产生,如果定义对象的时候就把对象定义成 const,那么都不能插入数据了
#include <iostream>
using namespace std;
#include <vector>
//const 正向迭代器,const不能写,可以读
void print_vector(const vector<int>& vt)
{
vector<int>::const_iterator it = vt.begin();
while (it != vt.end())
{
//*it = 1; 不能写
cout << *it << " ";
++it;
}
cout << endl;
}
//const反向迭代器,
void print_rvector(const vector<int>& vt)
{
vector<int>::const_reverse_iterator rit = vt.rbegin();
while (rit != vt.rend())
{
//不能写(修改)
cout << *rit << " ";
++rit;
}
cout << endl;
}
void test_vector5()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
//1.正向迭代器
vector<int>::iterator it = v.begin();
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//3.const 正向迭代器
print_vector(v);
//4.const 反向迭代器
print_rvector(v);
//2.反向迭代器
vector<int>::reverse_iterator rit = v.rbegin();
while (rit != v.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
}
int main()
{
test_vector5();
return 0;
}
6.容量相关的接口
1. 在 VS 中容量的增长是按1.5倍增长的
#include <iostream>
using namespace std;
#include <vector>
void test_vector6()
{
vector<int> v;
size_t sz = v.capacity(); // sz 记录初始容量
cout << "making v grow:\n";
for (int i = 0; i < 100; ++i)
{
v.push_back(i); //不断增加数据导致 capacity 发生改变,即增容
if (sz != v.capacity()) //不相等表示还没增容不进去打印,更新的容量
{
sz = v.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
int main()
{
test_vector6();
return 0;
}
2 .在Linux中,则是按 2 倍增长的
3. vector 插入数据是如何实现的,为什么增容增加 2倍,1.5倍不行吗?
1. 当空间足够的时候:进行插入操作(如
push_back
)时,首先会检查当前已分配的内存空间是否足够容纳新元素。如果空间足够,直接将新元素放置在合适的位置,并更新相关的指针和计数器(如_finish
)。当空间不足的时候:如果当前容量不足,就需要进行扩容操作。通常,
vector
会分配一块更大的连续内存空间,将原有元素复制到新的内存空间中,然后添加新元素,并更新相关的指针和计数器。2. 以 2 倍的方式扩容,可以减少频繁扩容的次数。如果每次只增加较小的比例(如 1.5 倍),在数据量较大且频繁插入的情况下,可能会导致更多次的扩容操作,而每次扩容都涉及到内存分配、元素复制等开销较大的操作。以较大的比例扩容可以在一定程度上平衡内存使用和性能开销。
如果采用 1.5 倍的扩容策略,相对 2 倍扩容来说,在一定程度上可能会减少一些空间浪费。但是需要更频繁地进行扩容操作。因为每次扩容增加的空间相对较小,当数据持续插入时,可能会比 2 倍扩容更快地达到容量上限,从而导致更多次的内存分配和数据复制操作,这会带来一定的性能销。
综上所述:C++ 中 vector 常见的扩容策略有 2 倍和 1.5 倍。2 倍扩容减少频繁扩容次数,计算简单但可能浪费空间。1.5 倍扩容空间利用更紧凑,但可能导致更频繁扩容操作且计算复杂些。具体策略取决于实际应用场景对性能和内存的需求。
迭代器失效(☆)
情景一:
增容就导致迭代器失效了,我都已经开了新空间,迭代器还指向旧空间,所以迭代器失效了(无法使用该迭代器)。
push_back,insert,resize,reserve 等可能会扩容的都会导致迭代器失效。
#include <iostream>
using namespace std;
#include <vector>
void test_vector7()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
vector<int>::iterator it = v.begin();
v.push_back(6);
v.push_back(7); //插入7就有问题了
while (it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
int main()
{
test_vector7();
return 0;
}
如何解决: ① 我们可以在插入完了之后,然后获取迭代器避免这种情况的发生。
② 我们就是要以上代码的情况:我们需要再次获取迭代器 it = v.begin();
情景二:
在 VS 中,当执行了 erase 删除操作之后,迭代器就已经失效了,再执行 ++it,正好跳过了奇数 3,假设不是3,是30呢?也就是说,这里跳过了一次判断, 在VS中报错,是编译检查的。
Linux下编译不会报错,但是执行的结果可能侥幸通过,g++ vector.cpp –std=c++11
- 段错误,删除6的时候有越界行为,我在加一行 push_back(7); 不会越界,可以执行出结果。
- 连续的偶数的时候,下一个也不会删除。
#include <iostream>
using namespace std;
#include <vector>
void test_vector8()
{
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>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
v.erase(it); //传的就是迭代器
}
++it;
}
for (auto e : v)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
test_vector8();
return 0;
}
Linux中的结果:编译没有检查出错误,但是在执行的时候出现,段错误,是因为最后删除6的时候越界了。这只能说明Linux中没有进行严格的检查。
gcc 下面没有进行严格的检查,可能会报错,可能会正常运行,也可能有个别偶数没被删除掉。
总结: 不管哪个平台下,erase(it) 以后,it 就失效了,只是导致的结果不一样而己,总之都有各种各样的问题。
如何解决呢???
erase 有一个返回值,返回的是删除的 it 的下一个位置的迭代器
删除一个元素后,将这个新的迭代器赋值给
it
,确保迭代器始终指向一个有效的元素,避免出现迭代器失效的情况。
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
it = v.erase(it); //传的就是迭代器
}
else
{
++it;
}
}
vector的模拟实现
源码的引入
以前我们定义结构的时候都是定义一个指针和_size、_capacity,而源码中使用的是三个指针,分别表示类似的意思: 指向动态数组,记录有效个数,记录容量,容量不够则需要增容。
基本结构的搭建
vector 的模拟实现和 string类 类似,都是在一个命名空间中定义。防止与标准库中的 vector 命名发生冲突。
vector.h
#pragma once
namespace my_vector
{
template<class T>
class vector
{
public:
typedef T* iterator; //假设T是int, iterator 就是 int*
typedef const T* const_iterator; //const 迭代器
//迭代器
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
//const迭代器
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
//构造函数
vector()
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{}
//析构函数
~vector()
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
size_t size() const //当对象传参给引用的时候,会加const,遍历数据调用size,const只能调用const
{
return _finish - _start;
}
size_t capacity() const //和size同理
{
if (_start == nullptr && _finish == nullptr) //第一次push数据的时候,两个空指针相减,不一定是0,保险起见,返回0;
return 0;
return _endofstorage - _start;
}
T& operator[](size_t i)
{
assert(i < size());
return _start[i];
}
const T& operator[](size_t i) const
{
assert(i < size());
return _start[i];
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
}
vector的模拟实现
push_back 尾插
该逻辑和顺序表的尾插是一样的,空间不够增容,空间够了,直接尾插。
同时指针 _finish 向后移动一步
//尾插
void push_back(const T& x)
{
if (_finish == _endofstorage)
{
size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
reserve(newcapacity); //接下来实现 reserve
}
*_finish = x;
++_finish;
}
reserve 增容
开临时空间,拷贝数据到临时空间,释放旧空间,更新三个指针。
在这里如果这样写,会存在指针失效的问题,当我们开了新的空间,而 _size任然指向旧空间,size() = _size - _start ,_start在新空间,而_size在旧空间,一相减就会出现问题,不同空间的指针相减属于未定义的行为,计算capacity也是同理: _endofstorage - _start 也分别指向不同的空间。
//3,reserve增容
void reserve(size_t n)
{
if (n > capacity())
{
T* tmp = new T[n];
if (_start)
{
memcpy(tmp, _start, sizeof(T) * size());
delete[] _start;
}
_start = tmp;
_finish = tmp + size();
_endofstorage = tmp + capacity();
}
}
如何解决
我们在开新空间之间就把 size计算好,这样就可以更新_finish 和 _endofstorage,这样就解决了指针失效的问题。
//3,reserve增容
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
memcpy(tmp, _start, sizeof(T) * sz);
delete[] _start;
}
_start = tmp;
_finish = tmp + sz;
_endofstorage = tmp + n;
}
}
pop_back 尾删
//7,pop_back()
void pop_back()
{
assert(_start < _finish); //保证可以有数据删除
--_finish;
}
insert 任意位置的插入
同样会存在指针失效的问题。
当增容的时候 pos 失效了,没有扩容前 pos指向旧空间,扩容后pos指针还指向旧的空间,所以需要先计算好 pos 到_start 的距离。
void insert(iterator pos, const T& x)
{
assert(pos <= _finish);
if (_finish == _endofstorage)
{
size_t n = pos - _start;
size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
reserve(newcapacity);
pos = _start + n;
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
}
erase 任意位置的删除
iterator erase(iterator pos)
{
assert(pos < _finish);
iterator it = pos;
while (it < _finish)
{
*it = *(it + 1);
++it;
}
--_finish;
return pos; //删除之后正好是下一个,直接返回pos
}
resize的实现
如果
T
是一个内置类型(如int
、double
等),那么T()
会产生一个相应类型的默认初始化值(对于整数通常是 0,对于浮点数通常是 0.0 等)。string 类已经详解介绍了resize 的实现,逻辑都是一样的。
实现resize时候,为什么不能用memcpy 或 memset,按字节拷贝或设置,除了 0 给任何值都达不到这样的效果,因为mem系列的函数都是按字节处理。
0000 0000 0000 0000 0000 0000 0000 0000
如果是 1
0000 0001 0000 0001 0000 0001 0000 0001 = 很大的数字,而不是 1 这个值
所以 memset 只适合初始化为0
// resize(),不仅仅开空间,还初始化
void resize(size_t n, const T& val = T()) //给T类型的缺省值的意思
{
if (n <= size())
{
_finish = _start + n;
}
else
{
//增容
if (n > capacity())
{
reserve(n);
}
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
}
拷贝构造
vector中也存在深浅拷贝的问题。
没有写拷贝构造就是浅拷贝,析构的时候就会析构两次。
拷贝构造也是构造的一种,开同样的大空间去初始化,_finsh 随着增加的值而变化。
方法一:
//拷贝构造 v2(v1)
vector(const vector<T>& v) //const去调用,只能调用const,这就是为什么 capacity 和 size 加 const的原因
{
_start = new T[v.capacity()];
_finish = _start;
_endofstorage = _start + v.capacity();
for (size_t i = 0; i < v.size(); ++i)
{
*_finish = v[i]; //因为v是const,所以调用 operator[]() const
++_finish;
}
}
方法二:
// 拷贝构造 方法二
vector(const vector<T>& v)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
reserve(v.capacity()); //先开好空间
for (const auto& e : v)
{
push_back(e); //push的过程中 _finish,和_endofstorage随之变化
}
}
operator= 赋值
传统写法:
//赋值 operator=
vector<T>& operator=(const vector<T>& v)
{
if (this != &v)
{
delete[] _start;
_start = new T[v.capacity()];
size_t sz = v.size();
/*for (size_t i = 0; i < sz; ++i)
{
_start[i] = v[i];
}*/
memcpy(_start, v._start, sizeof(T) * sz); //少使用mem系列,因为是按字节处理的
_finish = _start + sz;
_endofstorage = _start + v.capacity();
}
return *this;
}
现代写法:
//赋值现代写法===>建议写这个
vector<T>& operator=(vector<T> v) //先调用构造函数初始化 v
{
swap(v); //this->swap(v);
return *this;
}
void swap(vector<T>& v)
{
std::swap(_start, v._start); //我没展开std,如果展开了就写成 ::swap表示调用的是全局的swap
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
memcpy拷贝问题
假设我们在 vector 中存储的是字符串
vector<std::string> v;
v.push_back("1111111111111111111111111111111");
v.push_back("2222222222222222222222222222");
v.push_back("3333333333333333333333333333");
v.push_back("4444444444444444444444444444");for (auto e : v)
{
std::cout << e << " ";
}
我们在插入string 类型的时候,字符串存在于堆上,当vector容量不够的时候,需要增容,而在 reserve 中,我们增容的时候把旧空间的数据拷贝过去,我们使用的是memcpy拷贝,memcpy是按字节拷贝,也就是浅拷贝,_start 也会指向那块空间,当我们指向 delete[] _start的时候,空间被释放了,新空间中的_start 还指向被释放的空间,一个指向被释放空间的指针就是野指针,当程序结束后还要执行析构,还会释放该空间,所以不能使用memcpy去拷贝。 正常情况下,我的delete没错,是memcpy导致我们两指针都指向了同一块空间。
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
memcpy(tmp, _start, sizeof(T) * sz);//按字节拷贝,浅拷贝
delete[] _start;
}
_start = tmp;
_finish = tmp + sz;
_endofstorage = tmp + n;
}
}
如何解决? 不能使用 memcpy 进行拷贝
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
//memcpy(tmp, _start, sizeof(T) * sz);//按字节拷贝,浅拷贝
for (size_t i = 0; i < sz; ++i)
{
tmp[i] = _start[i]; // 调用的是T的 operator=深拷贝
}
delete[] _start;
}
_start = tmp;
_finish = tmp + sz;
_endofstorage = tmp + n;
}
}
整体代码
vector.h
#pragma once
namespace my_vector
{
template<class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;
vector()
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{}
拷贝构造 v2(v1)
//vector(const vector<T>& v) //const去调用,只能调用const
//{
// _start = new T[v.capacity()];
// _finish = _start;
// _endofstorage = _start + v.capacity();
// for (size_t i = 0; i < v.size(); ++i)
// {
// *_finish = v[i]; //调用[] const
// ++_finish;
// }
//}
// 拷贝构造 方法二
vector(const vector<T>& v)
:_start(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{
reserve(v.capacity()); //先开好空间
for (const auto& e : v)
{
push_back(e);
}
}
赋值 operator=
//vector<T>& operator=(const vector<T>& v)
//{
// if (this != &v)
// {
// delete[] _start;
// _start = new T[v.capacity()];
// size_t copySize = v.size();
// /*for (size_t i = 0; i < copySize; ++i)
// {
// _start[i] = v[i];
// }*/
// memcpy(_start, v._start, sizeof(T) * v.size());
// _finish = _start + copySize;
// _endofstorage = _start + v.capacity();
//
// }
// return *this;
//}
//赋值现代写法===>建议写这个
vector<T>& operator=(vector<T> v)
{
swap(v);
return *this;
}
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_endofstorage, v._endofstorage);
}
//析构函数
~vector()
{
delete[] _start;
_start = _finish = _endofstorage = nullptr;
}
//6,const_iterator
const_iterator begin() const
{
return _start;
}
const_iterator end() const
{
return _finish;
}
//4,迭代器
iterator begin()
{
return _start;
}
iterator end()
{
return _finish;
}
//3,reserve增容
void reserve(size_t n)
{
if (n > capacity())
{
size_t sz = size();
T* tmp = new T[n];
if (_start)
{
//memcpy(tmp, _start, sizeof(T) * sz);//按字节拷贝,浅拷贝
for (size_t i = 0; i < sz; ++i)
{
tmp[i] = _start[i]; // 调用的是T的 operator=深拷贝
}
delete[] _start;
}
_start = tmp;
_finish = tmp + sz;
_endofstorage = tmp + n;
}
}
//10,resize(),不仅仅开空间,还初始化
void resize(size_t n, const T& val = T()) //给个T类型的缺省值的意思
{
if (n <= size())
{
_finish = _start + n;
}
else
{
//增容
if (n > capacity())
{
reserve(n);
}
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
}
//1,尾插
void push_back(const T& x)
{
/*if (_finish == _endofstorage)
{
size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
reserve(newcapacity);
}
*_finish = x;
++_finish;*/
insert(_finish, x);
}
//7,pop_back()
void pop_back()
{
/*assert(_start < _finish);
--_finish;*/
erase(_finish - 1); //干掉最后一个位置
}
//8,insert()
void insert(iterator pos, const T& x)
{
assert(pos <= _finish);
if (_finish == _endofstorage)
{
size_t n = pos - _start;
size_t newcapacity = capacity() == 0 ? 2 : capacity() * 2;
reserve(newcapacity);
pos = _start + n;
}
iterator end = _finish - 1;
while (end >= pos)
{
*(end + 1) = *end;
--end;
}
*pos = x;
++_finish;
}
//9,erase
iterator erase(iterator pos)
{
assert(pos < _finish);
iterator it = pos;
while (it < _finish)
{
*it = *(it + 1);
++it;
}
--_finish;
return pos; //删除之后正好是下一个
}
//5,operator[]
T& operator[](size_t i)
{
assert(i < size());
return _start[i]; //*(_start + i)
}
const T& operator[](size_t i) const
{
assert(i < size());
return _start[i];
}
//2,计算size()、capacity()
size_t size() const
{
return _finish - _start;
}
size_t capacity() const
{
if (_start == nullptr && _endofstorage == nullptr)
return 0;
return _endofstorage - _start;
}
private:
iterator _start;
iterator _finish;
iterator _endofstorage;
};
void print_vector(const vector<int>& v) //传参引用接受、就会加const
{
vector<int>::const_iterator it = v.begin();
while (it != v.end())
{
//*it += 1;
std::cout << *it << " ";
++it;
}
std::cout << std::endl;
}
void test_vector1()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
print_vector(v);
vector<int>::const_iterator it = v.begin();
while (it != v.end())
{
//*it += 1;
std::cout << *it << " ";
++it;
}
std::cout << std::endl;
for (auto& e : v)
{
//e -= 1; 要修改就用引用 auto&
std::cout << e << " ";
}
std::cout << std::endl;
for (size_t i = 0; i < v.size(); ++i)
{
std::cout << v[i] << " ";
}
std::cout << std::endl;
}
void test_vector2()
{
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);
v.insert(v.begin(), 0);
print_vector(v);
vector<int>::iterator it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
{
it = v.erase(it);
}
else
{
++it;
}
}
print_vector(v);
}
void test_vector3()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.resize(10);
print_vector(v);
std::cout << v.size() << std::endl;
std::cout << v.capacity() << std::endl;
v.resize(4);
print_vector(v);
std::cout << v.size() << std::endl;
std::cout << v.capacity() << std::endl;
v.push_back(5);
v.resize(12,12);
print_vector(v);
std::cout << v.size() << std::endl;
std::cout << v.capacity() << std::endl;
}
void test_vector4()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
/* vector<int> v2(v1);
for (size_t i = 0; i < v1.size(); ++i)
{
std::cout << v1[i] << " ";
}
std::cout << std::endl;
for (size_t i = 0; i < v2.size(); ++i)
{
std::cout << v2[i] << " ";
}
std::cout << std::endl;*/
//v1 = v3
vector<int> v3;
v3.push_back(10);
v3.push_back(20);
v3.push_back(30);
v3.push_back(40);
v1 = v3;
for (auto e : v1)
{
std::cout << e << " ";
}
std::cout << std::endl;
}
void test_vector5()
{
vector<std::string> v;
v.push_back("1111111111111111111111111111");
v.push_back("2222222222222222222222222222");
v.push_back("3333333333333333333333333333");
v.push_back("4444444444444444444444444444");
for (auto e : v)
{
std::cout << e << " ";
}
}
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <assert.h>
//using namespace std;
#include <string>
#include "vector.h"
int main()
{
//my_vector::test_vector1();
//my_vector::test_vector2();
//my_vector::test_vector3();
//my_vector::test_vector4();
my_vector::test_vector5();
return 0;
}
动态二维数组的理解
其实这就是C语言中的二维数组,只不过这里换成了vector,可以动态增长。