前言
vector是STL中的重要容器,在平时使用中较常见。学会使用它以及了解它的核心原理对于我们学习它是很有帮助的。vector是可以动态增长的数组。vector可以像数组一样进行随机访问,这是vector最大的优势之一,因为支持随机访问就间接的支持了排序,二分查找等。这样可以更好的管理数据。vector也有一些缺陷除了尾插尾删之外,其他位置的插入和删除都需要挪动数据,因此会消耗线性的时间复杂度,所以效率较低,vector插入数据当内存不足的时候需要扩容,扩容也是有代价的。
目录
1.构造函数
2.迭代器
3.容量空间
3.1容量空间
3.2空间增长问题
4.增删查改
5.迭代器失效
5.1迭代器失效
5.2解决方法
1.构造函数
例如:
void testVector()
{
vector<int> v1;//无参的构造函数
vector<int> v2(5, 2);//构造有5个2的list
vector<int> v3(v2);//拷贝构造
}
2.迭代器
iterator的使用 | 接口说明 |
begin+end | begin()是获取第一个元素的迭代器,end()是获取最后一个元素的下一个位置的迭代器 |
rbegin+rend | rbegin()是获取最后一个元素的迭代器,end()是获取第一个元素的上一个位置的迭代器 |
例如:
void print(const vector<int>& v1)
{
vector<int>::const_iterator cit = v1.cbegin();//const修饰的迭代器
while (cit != v1.cend())
{
cout << *cit << " ";
cit++;
}
cout << endl;
}
void testVector2()
{
vector<int> v1;//无参的构造函数
//尾插
v1.push_back(0);
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
v1.push_back(7);
//正向迭代器遍历
vector<int>::iterator it = v1.begin();
while (it != v1.end())
{
cout << *it << " " ;
it++;
}
cout << endl;
//逆向迭代器
vector<int>::reverse_iterator rit = v1.rbegin();
while (rit != v1.rend())
{
cout << *rit << " " ;
rit++;
}
cout << endl;
print(v1);
}
3.容量空间
3.1容量空间
容量空间 | 接口说明 |
size | 获取数据个数 |
capacity | 获取容量大小 |
empty | 判断vector是否为空 |
resize | 改变vector的size |
reserve | 改变vector的capacity |
例如:
void testVector5()
{
vector<int>v1;
v1.push_back(0);
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
cout << v1.size() << endl;//打印v1的size
cout << v1.capacity() << endl;//打印v1的capacity
cout << v1.empty() << endl;//判断v1是否为空
v1.resize(5);//改变vector的size
for (auto e : v1)
cout << e<<" ";
cout << endl;
v1.resize(10,6);
for (auto e : v1)
cout << e << " ";
cout << endl;
v1.reserve(20);//改变vector的容量
for (auto e : v1)
cout << e << " ";
cout << endl;
cout << v1.capacity() << endl;
}
3.2空间增长问题
我们一起来看看下面的代码:
int main()
{
//测试vector的扩容机制
vector<int> v1;
size_t sz = v1.capacity();
cout << "是否扩容" << endl;
for (int i = 0; i < 1000; i++)
{
v1.push_back(i);
if (sz != v1.capacity())
{
cout << "capacity changed: " ;
cout << sz << endl;
sz = v1.capacity();
}
}
return 0;
}
在vs下的结果:
是否扩容
capacity changed: 0
capacity changed: 1
capacity changed: 2
capacity changed: 3
capacity changed: 4
capacity changed: 6
capacity changed: 9
capacity changed: 13
capacity changed: 19
capacity changed: 28
capacity changed: 42
capacity changed: 63
capacity changed: 94
capacity changed: 141
capacity changed: 211
capacity changed: 316
capacity changed: 474
capacity changed: 711在g++下的运行结果:
capacity changed: 1
capacity changed: 2
capacity changed: 4
capacity changed: 8
capacity changed: 16
capacity changed: 32
capacity changed: 64
capacity changed: 128capacity changed: 256
capacity changed: 512
capacity changed: 1024
我们发现在不同的编译器下面vector的增长速度是不同的,vs下面是1.5倍,g++下面是2倍。
vs是PJ版本的STL,g++是SGI版本的STL。 vector的扩容速度不是固定的,要根据具体需求来定 ,扩容也不是越快越好,每次扩容的倍数越多,可能被浪费的空间也就越多,但是如果扩容的倍数较小的话,插入数据频繁时要多次扩容。扩容时开辟新空间,拷贝数据,释放旧空间都是有代价的。
reserve可以改变容量如果我们预先知道要插入多少数据,直接开好空间,就不需要扩容了。如下:
int main()
{
vector<int> v1;
v1.reserve(1000);//先开辟好要插入数据的空间
size_t sz = v1.capacity();
cout << sz;
for (int i = 0; i < 1000; i++)
{
v1.push_back(i);
if (sz != v1.capacity())//判断容量是否增加
{
cout << "capacity changed: "
cout << sz << endl;
sz = v1.capacity();//将新的capacity赋给sz
}
}
return 0;
}
4.增删查改
vector增删查改 | 接口说明 |
push_back | 尾插 |
pop_back | 尾删 |
find | 查找(这个是算法模块的实现,不是vector的成员接口) |
insert | 任意位置的插入(在position之前插入val ) |
erase | 删除position位置 |
swap | 交换两个vector的数据空间 |
operator[] | 像数组一样访问 |
例如:
void testVector3()
{
vector<int>v1;
//尾插尾删
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
v1.push_back(7);
v1.pop_back();
for (auto e : v1)
cout << e << " ";
cout << endl;
v1.insert(v1.begin(), 5, 7);//头插5个7
for (auto e : v1)
cout << e << " ";
cout << endl;
v1.insert(v1.begin() + 5, 9);//position位置插入9
for (auto e : v1)
cout << e << " ";
cout << endl;
//删除position位置的元素
v1.erase(v1.begin());
v1.erase(v1.begin()+1);
v1.erase(v1.begin())+3;
for (auto e : v1)
cout << e << " ";
cout << endl;
vector<int>::iterator it = find(v1.begin(), v1.end(), 9);//查找val值,返回的是val值所对应的迭代器
if (it != v1.end())//确保迭代器有效
{
*it = 20;
}
for (int i = 0; i < v1.size(); i++)
{
cout << v1[i] << " ";
}
cout << endl;
}
void testVector4()
{
vector<int> v1(5,2);
vector<int> v2(2,5);
for (auto e : v1)//打印v1
cout << e;
cout << endl;
for (auto e : v2)//打印v2
cout << e;
cout << endl;
swap(v1, v2);//交换v1和v2
for (auto e : v1)//打印v1
cout << e;
cout << endl;
for (auto e : v2)//打印v2
cout << e;
cout << endl;
}
5.迭代器失效
5.1迭代器失效
你是否遇到过这样的代码:
int main()
{
vector<int>v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
v1.push_back(6);
v1.push_back(7);
vector<int>::iterator it = v1.begin();
v1.push_back(8);
v1.push_back(9);
v1.push_back(10);
v1.push_back(11);
while (it != v1.end())
{
cout << *it << " ";
it++;
}
return 0;
}
运行上面的这段代码程序就会奔溃。为什么呢?
这就要涉及到迭代器失效的问题了。因为取出迭代器之后还对vector进行的尾插操作,尾插使得vector增容(增容会开辟新空间,拷贝数据释放旧空间)。此时的迭代器指向的还是旧的空间,此时对迭代器进行操作经已是不合法的了。
请接着往下看:
int main()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(8);
v1.push_back(6);
//删除v1中的偶数
vector<int> ::iterator it = v1.begin();
while (it !=v1.end())
{
if (*it % 2 == 0)
{
v1.erase(it);
}
else
{
++it;
}
}
for (auto e : v1)
cout << e << " ";
return 0;
}
上面的这段代码在vs下运行程序会奔溃,因为删除it以后,it就失效了,这里是指it的位置失效了,再解引用或者++就不行了,vs的编译器对此进行了严格的检查所有会报错,但是在g++没有进行严格的语法检查,可以运行这个程序。
总结,不管在哪个平台下,erase(it)以后it就失效了,只是导致的结果不一样而已。
改进,erase(it)以后 接收erase的返回值,因为erase返回的是紧挨着it位置的下一个位置的迭代器。
如下:
int main() { vector<int> v1; v1.push_back(1); v1.push_back(2); v1.push_back(3); v1.push_back(4); v1.push_back(8); v1.push_back(6); //删除v1中的偶数 vector<int> ::iterator it = v1.begin(); while (it !=v1.end()) { if (*it % 2 == 0)//迭代器失效,it的位置不对了,++就不行,编译器检查的结果,g++下不一定会报错,导致结果不对 { v1.erase(it); } else { ++it; } } for (auto e : v1) cout << e << " "; return 0; }
5.2解决方法
凡是涉及erase,insert,pop_back,push_bakc等有关内存的操作都可能导致迭代器失效。对于上述第一种问题,只有在将要使用迭代器的时候再去求迭代器。在使用迭代器之前要对迭代器进行赋值。就可以解决了。