C++ 顺序容器(一)

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

猜你喜欢

转载自blog.csdn.net/hc372893308/article/details/83038260