vector容器、迭代器、基于范围的for循环

vector 容器封装了动态数组。
包含头文件: #include<vector>
vector 类模板的声明:

template<class T, class Alloc = allocator<T>>
class vector{
private:
T *start_;
T *finish_;
T *end_;
......
};

分配器
各种 STL 容器模板都接受一个可选的模板参数,该参数指定使用哪个分配器对象来管理内存
如果省略该模板参数的值,将默认使用 allocator<T>,用 new 和 delete 分配和释放内存。

一、构造函数

1)vector(); // 创建一个空的 vector 容器。
2)vector(initializer_list<T> il); // 使用统一初始化列表。
3)vector(const vector<T>& v); // 拷贝构造函数。
4)vector(Iterator first, Iterator last); // 用迭代器创建 vector 容器。
5)vector(vector<T>&& v); // 移动构造函数(C++11 标准)。

不重要的已省略。

析构函数~vector()释放内存空间。

二、特性操作

1)size_t max_size() const; // 返回容器的最大长度,此函数意义不大。
2)size_t capacity() const; // 返回容器的容量。
3)size_t size() const; // 返回容器的实际大小(已使用的空间)。
4)bool empty() const; // 判断容器是否为空。
5)void clear(); // 清空容器。
6)void reserve(size_t size); // 将容器的容量设置为至少 size。
7)void shrink_to_fit(); // 将容器的容量降到实际大小(需要重新分配内存)。
8)void resize(size_t size); // 把容器的实际大小置为 size。

三、元素操作

1)T &operator[](size_t n);
2)const T &operator[](size_t n) const; // 只读。
3)T &at(size_t n);
4)const T &at(size_t n) const; // 只读。

以上均为返回第n个元素
5)T *data(); // 返回容器中动态数组的首地址。
6)const T *data() const; // 返回容器中动态数组的首地址。
7)T &front(); // 第一个元素。
8)const T &front(); // 第一个元素,只读。
9)const T &back(); // 最后一个元素,只读。

10)T &back(); // 最后一个元素。

四、赋值操作

给已存在的容器赋值,将覆盖容器中原有的内容。
1)vector &operator=(const vector<T> &v); // 把容器 v 赋值给当前容器。
2)vector &operator=(initializer_list<T> il); // 用统一初始化列表给当前容器赋值。
3)void assign(initializer_list<T> il); // 使用统一初始化列表赋值。
4)void assign(Iterator first, Iterator last); // 用迭代器赋值。
5)void assign(const size_t n, const T& value); // 把 n 个 value 给容器赋值

vector<int> v1;
v1 = { 1,2,3,4,5 }; // 使用统一初始化列表赋值。
for (int ii = 0; ii < v1.size(); ii++) cout << v1[ii] << " ";
cout << endl;
vector<int> v2;
v2 = v1; // 把容器 v1 赋值给当前容器。
for (int ii = 0; ii < v2.size(); ii++) cout << v2[ii] << " ";
cout << endl;
vector<int> v3;
v3.assign({ 1,2,3,4,5 }); // 用 assign()函数给当前容器赋值,参数是统一初始化列表。
for (int ii = 0; ii < v3.size(); ii++) cout << v3[ii] << " ";
cout << endl;

五、交换操作

void swap(vector<T> &v); // 把当前容器与 v 交换。
交换的是动态数组的地址。

六、比较操作

bool operator == (const vector<T> & v) const;
bool operator != (const vector<T> & v) const;

七、插入和删除

1)void push_back(const T& value); // 在容器的尾部追加一个元素。
2)void emplace_back(...); // 在容器的尾部追加一个元素,...用于构造元素。C++11
3)iterator insert(iterator pos, const T& value); // 在指定位置插入一个元素,返回指向插入元
素的迭代器。
4)iterator emplace (iterator pos, ...); // 在指定位置插入一个元素,...用于构造元素,返回指向
插入元素的迭代器。C++11
5)iterator insert(iterator pos, iterator first, iterator last); // 在指定位置插入一个区间的元素,
返回指向第一个插入元素的迭代器。
6)void pop_back(); // 从容器尾部删除一个元素。
7)iterator erase(iterator pos); // 删除指定位置的元素,返回下一个有效的迭代器。
8)iterator erase(iterator first, iterator last); // 删除指定区间的元素,返回下一个有效的迭代器。

强烈建议使用emplace.

八、嵌套使用

vector<vector<int>> vv; // 创建一个 vector 容器 vv,元素的数据类型是 vector<int>。
vector<int> v; // 创建一个容器 v,它将作为容器 vv 的元素。

九、迭代器失效的问题

resize()、reserve()、assign()、push_back()、pop_back()、insert()、erase()等函数会引起 vector
容器的动态数组发生变化,可能导致 vector 迭代器失效。

十、补充

1.什么是迭代器?

迭代器是访问容器中元素的通用方法。
如果使用迭代器,不同的容器,访问元素的方法是相同的。
迭代器支持的基本操作:赋值(=)、解引用(*)、比较(==和!=)、从左向右遍历(++)。
一般情况下,迭代器是指针和移动指针的方法。

迭代器一般有五种:

1)正向迭代器

只能使用++运算符从左向右遍历容器,每次沿容器向右移动一个元素。
容器名<元素类型>::iterator 迭代器名; // 正向迭代器。
容器名<元素类型>::const_iterator 迭代器名; // 常正向迭代器。
相关的成员函数:
iterator begin();
const_iterator begin();
const_iterator cbegin(); // 配合 auto 使用。
iterator end();
const_iterator end();
const_iterator cend();

2)双向迭代器

具备正向迭代器的功能,还可以反向(从右到左)遍历容器(也是用++),不管是正向还是反向遍历,都可以用--让迭代器后退一个元素。

容器名<元素类型>:: reverse_iterator 迭代器名; // 反向迭代器。
容器名<元素类型>:: const_reverse_iterator 迭代器名; // 常反向迭代器。
相关的成员函数:
reverse_iterator rbegin();
const_reverse_iterator crbegin();
reverse_iterator rend();
const_reverse_iterator crend();

3)随机访问迭代器
具备双向迭代器的功能,还支持以下操作:
? 用于比较两个迭代器相对位置的关系运算(<、<=、>、>=)。
? 迭代器和一个整数值的加减法运算(+、+=、-、-=)。
? 支持下标运算(iter[n])。
数组的指针是纯天然的随机访问迭代器。

4)输入和输出迭代器
这两种迭代器比较特殊,它们不是把容器当做操作对象,而是把输入/输出流作为操作对象。

#include <iostream>
#include <vector>
#include <list>
using namespace std;
struct Node // 单链表的结点。
{
int item;
Node* next;
};
int* find_(int* arr, int n, const int& val) // 在整型数组 arr 中查找值为 val 的元素。
{
for (int ii = 0; ii < n; ii++) // 遍历数组。
if (arr[ii] == val) return &arr[ii]; // 如果找到了,返回数组中元素的地址。
return nullptr;
}
int* find_(int* begin, int* end, const int& val) // 在整型数组的区间中查找值为 val 的元素。
{
for (int* iter = begin; iter != end; iter++) // 遍历查找区间。
if (*iter == val) return iter; // 如果找到了元素,返回区间中的
位置。
return nullptr;
}
Node* find_(Node* begin, Node* end, const Node& val) // 在单链表中查找值为 val
的元素。
{
for (Node * iter = begin; iter != end; iter = iter->next) // 遍历链表。
if (iter->item == val.item) return iter; // 如果找到了,返回链表中结点的地址。
return nullptr;
}
// 查找元素的算法。
template<typename T1, typename T2>
// begin-查找区间开始的位置;end-查找区间结束的位置;val-待查找的值。
T1 find_(T1 begin, T1 end, const T2 &val)
{
for (T1 iter = begin; iter != end; iter++) // 遍历查找区间。
if (*iter == val) return iter; // 如果找到了元素,返回区间中的位
置。
return end;
}
int main()
{
// 在 vector 容器中查找元素。
vector<int> vv = { 1,2,3,4,5 }; // 初始化 vector 容器。
vector<int>::iterator it2 = find_(vv.begin(), vv.end(), 3);
if (it2 != vv.end()) cout << "查找成功。
";
else cout << "查找失败。
";
// 在 list 容器中查找元素。
list<int> ll = {1,2,3,4,5}; // 初始化 vector 容器。
list<int>::iterator it3 = find_(ll.begin(), ll.end(), 3);
if (it3 != ll.end()) cout << "查找成功。
";
else cout << "查找失败。
";
}

2.基于范围的for循环

对于一个有范围的集合来说,在程序代码中指定循环的范围有时候是多余的,还可能犯错误。
C++11 中引入了基于范围的 for 循环。
语法:

for (迭代的变量 : 迭代的范围)
{
// 循环体。
}

vector<int> vv = { 1,2,3,4,5,6,7,8,9,10 };
//for (auto it = vv.begin(); it != vv.end(); it++) // 用迭代器遍历容器 vv。
//{
// cout << *it << " ";
//}
//cout << endl;
for (auto &val : vv) // 用基于范围的 for 循环遍历数组 vv。
{
cout << val << " ";
vv.push_back(10);
}
cout << endl;

注意:
1)迭代的范围可以是数组名、容器名、初始化列表或者可迭代的对象(支持 begin()、end()、++、
==)。
2)数组名传入函数后,已退化成指针,不能作为容器名。
3)如果容器中的元素是结构体和类,迭代器变量应该申明为引用,加 const 约束表示只读。
4)注意迭代器失效的问题。

强烈建议加引用。