这是一个vector模板的声明,实际上实例化需要传入模板参数。
- 第一个是他的参数类型,例如
vector<int>
,vector<char>
,甚至一个二维数组vectoe<vector<int>>
,vector<vector<char>>
等。 - 第二个参数是空间配置器,空间配置器就是内存池,从内存池里拿内存,避免反复从操作系统申请。
不是直接new或malloc,而是从内存池这个中间商去拿,可以提高效率。作为参数就是可以让我们自定义自己的空间配置器,来替换STL默认的。
我们用的string是直接实例化好的一个模板类。我们目前所接触的string参数都是char类型,只是经过了typedef。
1. 构造函数
explict代表不允许隐式类型转换的发生,因为一个内存池变成vector对象,是很怪异的。
//无参构造
vector<int> v1;
//构造10个空间初始化10个3
vector<int> v2(10,3);
//拷贝构造
vector<int> v3(v2);
迭代器初始化构造需要注意的地方。
- 可以是其他类型的迭代器,但是属性必须和构造对象的属性相同。因为它实际上是一个模板
传什么迭代器就会推演出什么迭代器,认为迭代器是一段区间(左闭右开)但是当迭代器解引用之后必须为同一类型。
//迭代器初始化构造
vector<int> v4(v2.begin(), v2.end());
list<int> l1(5,4);
vector<int> v5(l1.begin(), l1.end());
//原理
while(first!=last)
{
push_back(*first);
++first;
}
2. 遍历
2.1 []遍历
for (size_t i = 0; i < v2.size(); i++)
{
cout << v2[i] <<" ";
}
2.2 迭代器遍历
与之前的string使用几乎一模一样
vector<int>::iterator it3 = v3.begin();
while (it3 != v3.end())
{
cout << *it3;
it3++;
}
同样需要注意的是在函数内,由于我们不需要改变可以加const且对象是引用,所以我们需要使用const迭代器
void print(const vector<int>& v)
{
vector<int>::const_iterator it3 = v.begin();
while (it3 != v.end())
{
cout << *it3;
it3++;
}
}
反向迭代器倒着遍历,每次的迭代器对象是++,并不是–。
2.3 范围for
for(const auto& e:v3)
{
cout<<v3<<" ";
}
替换成了迭代器。尽量传引用,不需要改变在加上const。因为v3可能是vector<string>,更有可能是vector<map<string,string>>等各种类型套一起
,如果不加引用,e每次都深拷贝消耗太大太大。
3. 空间
3.1 resize
当n小于原有空间。将原有空间缩小为n。
//v3.resize(5,100);
v3.resize(5);
resize,当n大于原有空间时,开空间并且初始化。所以当n小于原有空间,即使你第二个参数传了值,也不会改变以前的值。
v3.resize(12,5);
3.2 reserve
先来看一个增容问题
vector<int> v1;
int sz = v1.capacity();
for (size_t i = 0; i < 100; i++)
{
v1.push_back(i);
if (sz != v1.capacity())
{
sz = v1.capacity();
cout << "发生增容,现在vector的capacity为:"<<sz<< endl;
}
}
可以看出在vs编译器下几乎是以1.5倍取整一直增容。
同样的代码在Linux下是以2倍的增容。
没有什么大的区别,倍数小,增容次数频繁,倍数大就会有浪费空间,很正常。
所以其实当你知道也要开多大的空间时,你就直接调用reserve开空间。就不会有乱七八糟的扩容了
4. 增删改查
4.1 尾插与尾删
v1.push_back(5);
v1.push_back(6);
v1.push_back(7);
v1.push_back(8);
v1.push_back(9);
print(v1);
v1.pop_back();
print(v1);
4.2 查找
find太常见了,所以在std库中直接实现了一个模板。
返回一个迭代器,因为迭代器的区间是[firsr,last)
,所以返回last就代表没有找到。
//也可以直接用auto pos=find(v1.begin(), v1.end(), 5);
vector<int>::iterator pos = find(v1.begin(), v1.end(), 5);
//返回v1.end()就说明没找到
if(pos != v1.end())
{
cout << "找到了" << endl;
*pos = 30;
}
for (auto& e : v1)
{
cout << e << " ";
}
cout << endl;
找到并且修改
5. 插入
学会find之后这些接口就容易多了,因为他们都是在find接口之后进行操作。只是需要时刻注意迭代器的左闭右开,也就是begin()是0下标,end()的下标是size,也就是最后一个元素的下一个位置
vector<int>::iterator pos = find(v1.begin(), v1.end(), 5);
//返回v1.end()就说明没找到
if(pos != v1.end())
{
cout << "找到了" << endl;
v1.insert(pos, 4);
}
for (auto& e : v1)
{
cout << e << " ";
}
cout << endl;
vector<int>::iterator pos = find(v1.begin(), v1.end(), 5);
//返回v1.end()就说明没找到
if(pos != v1.end())
{
cout << "找到了" << endl;
/*v1.insert(pos, 4);*/
}
v1.erase(pos);
for (auto& e : v1)
{
cout << e << " ";
}
cout << endl;
这里必须把insert注释掉,因为插入会引起迭代器失效的问题,pos本来指向5,由于插入了一个4,导致pos的指向发生变化,引起迭代器失效。也可能是空间不够,由于增容的时候步骤是,先扩容,拷贝原空间至扩容的空间,然后释放原空间,pos此时还指向这原空间,迭代器也会失效。以后还会详细进行讲解。