1 概述
一个容器就是一些特定类型对象的集合。顺序容器为我们提供了控制元素存储和访问顺序的能力。这种顺序不依赖于元素的值,而是与元素加入容器时的位置相对应。
其中,vector和string将元素保存在连续的内存空间中。
下面解释上述的类型别名:
const iterator 是iterator常量,iterator本身里面存的是指针,也就是iterator的值,也就是那个指针不能改变,也就是不能指向其他的位置,但是所指向的位置的元素是可以通过这个iterator来改变的。
const_iterator 其实本质来说,是另一个类。我们可以想象成,它的数据成员是一个指向常量元素的指针,(比如 const T*)也就是说,这个const_iterator里存着的指针是可以改变的,即可以 ++ 或 - - 操作,但是,这是一个指向常量的指针,指向的元素是常量,不可改变。
vector<int> ivec1(10);
const vector<int> ivec2(10);
vector<int>::iterator iter1 = ivec1.begin(); // true
vector<int>::iterator iter2 = ivec2.begin(); // error
vector<int>::const_iterator iter3 = ivec2.begin(); // true
vector<int>::const_iteartor iter4 = ivec1.begin(); // true
size_t 是 unsigned 类型,用于指明数组长度或下标,它必须是一个正数, std::size_t
ptrdiff_t 是 signed 类型,用于存放同一数组中两个指针之间的差距,它可以使负数, std::ptrdiff_t.
size_type 是 unsigned 类型 , 表示容器中元素长度或者下标, vector<int>::size_type i = 0;
difference_type 是 signed 类型 , 表示迭代器差距, vector<int>:: difference_type = iter1-iter2.
前二者位于标准类库 std 内,后二者专为 STL 对象所拥有。
value_type 就是stl容器盛装的数据的数据类型。例如:
vector<int> vec;
vector<int>::value_type x;
上述两句代码,第一句是声明一个盛装数据类型是int的数据的vector,第二句是使用vector<int>::value_type定义一个变量x,这个变量x实际上是int类型的,因为vector<int>::value_type中声明的为int型。相应的,假设有:
vector<C> vec; //假设C是自定义类型
vector<C>::value_type x;
那么第二句定义的变量x的数据类型是C。
迭代器:类似于指针,数组下标。一个迭代器范围由一对迭代器表示,两个迭代器分别指向同一个容器中的元素或者是尾元素之后的位置。这两个迭代器通常被称为 begin 和 end,或者first或last。它们有个元素范围被称为左闭合区间,即
[begin, end)
注意:迭代器begin永远在end前面或者同位置。
begin和end有多个版本,如下:
容器定义和初始化
每个容器类型都定义了一个默认构造函数。除了 array 之外,其他容器的默认构造函数都会创建一个指定类型的空容器,且都可以接受指定容器大小和元素初始值的参数。
例子如下:
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> vec;
vector<int>::iterator it;
for (int i=0;i<10;i++){
vec.push_back(i);
}
vector<int> v1(vec);
vector<int> v2 = vec;
vector<int> v3{0,1,2,3,4,5,6,7,8,9};
vector<int> v4 = {0,1,2,3,4,5,6,7,8,9};
vector<int> v5(v1.begin(),v1.end());
vector<int> v6(10);
vector<int> v7(10,6);
string s[] = {"v1","v2","v3","v4","v5","v6","v7"};
for (int i=0;i<7;i++){
cout << s[i] << ": ";
for (int j=0;j<10;j++){
switch (i){
case 0: cout << v1[j] << " ";break;
case 1: cout << v2[j] << " ";break;
case 2: cout << v3[j] << " ";break;
case 3: cout << v4[j] << " ";break;
case 4: cout << v5[j] << " ";break;
case 5: cout << v6[j] << " ";break;
case 6: cout << v7[j] << " ";break;
default: break;
}
}
cout << endl;
}
return 0;
}
创建一个容器为另一个容器的拷贝,两个容器的类型及其元素类型必须匹配。不过,当传递迭代器参数来拷贝一个范围时,就不要求类型是相同的了。而且,新容器和原容器中的元素类型也可以不同,只要能将要拷贝的元素转换为要初始化的容器的元素类型即可。
标准库array
array是数组的升级版,将数组正式纳入到容器的范畴。array在使用和性能上都要强于内置数组,对于一些固定大小的使用场景,可以用array来替代原先数组的工作。array具有固定大小。
和数组不同的是,array可以使用拷贝和赋值的形式进行初始化:
array<int,10> ial;
array<int,10> ial1={0,1,2,3};
array<int,10> copy = ial1; //只要保证两者的类型一致就可以(包括元素类型和大小)
这点比传统内置数组的循环赋值效率高很多。
array的使用:
array<int,10> ia = {0,1,2,3,4,5,6,7,8,9}; // 初始化ia
array<int,10> ib = ia; // 拷贝ia到ib
cout << *(ib.begin()+4) << " " << ib[4] << endl; // 可使用迭代器、指针和下标
赋值和 swap
使用assign(仅顺序容器)
顺序容器(array除外)还定义了一个名为assign的成员,允许我们从一个不同但相容的类型赋值,或从容器的一个子序列赋值。assign操作用参数所指定的元素(的拷贝)替换左边容器中的所有元素。例子如下:
list<string> names;
vector<const char*> oldstyle;
names = oldstyle; // 错误:容器类型不匹配
// 正确:可以将cosnt char*转换为string
names.assign(oldstyle.cbegin(),oldstyle.cend());
注意:
1. 由于其旧元素被替换,因此传递给assign的迭代器不能指向assign的容器。
2. swap操作交换两个相同类型容器的内容。
作用于array时,swap两个array会真正交换它们的元素。因此,交换两个array所需的时间与array中的元素数量成正比。
而作用于其他容器时,swap不对任何元素进行拷贝、删除或插入操作,因此可以保证在常数时间内完成。
容器大小操作
只有当其元素类型也定义了相应的比较运算符时,我们才可以使用关系运算符来比较两个容器。
比较两个容器实际上是进行元素的逐对比较。与string的关系运算类似:
参考:
const_iterator: https://blog.csdn.net/Veahlin/article/details/68490756
difference_type size_type: https://blog.csdn.net/yhl_leo/article/details/47759729
value_type: https://blog.csdn.net/hzh2007/article/details/8671627
array: https://blog.csdn.net/u011405218/article/details/70653323