9.2容器库操作

容器库概述

1.容器操作

所有容器都提供的操作如下:

类型别名
iterator             此容器类型的迭代器类型
const_iterator       可以读取元素,但是不能修改元素的迭代器类型
size_type            无符号整数类型,足够保存此种容器类型最大可能容器的大小
value_type           元素类型
reference            元素的左值类型;于value_type含义相同
const_reference      元素的const左值类型(即const value_type &)

构造函数
C c;                 默认构造函数,构造空容器
C c1(c2);            拷贝构造,构造c2的拷贝c1
C c(b,e);            构造c,将迭代器b和e指定的范围内的元素拷贝到c(array不支持)
C c{a,b,c...};       列表初始化c

赋值与swap
c1 = c2;             将c1中的元素怒替换为c2中的元素
c1 = {a,b,c..};      将c1中的元素替换为列表中的元素(array不支持)
a.swap(b);           交换a与b的元素
swap(a,b);           与a.swap(b)等价

大小
c.size();            c中元素的数目(不支持forward_lis)
c.max_size();        c可保存的最大元素数目
c.empty();           c为空则为true,否则为false

添加/删除元素(不适用于array)
c.insert(args);      将args中的元素拷贝进c
c.emplace(inits);    使用inits构造c中的一个元素
c.erase(args);       删除args指定的元素
c.clear();           删除c中的所有元素,返回void

获取迭代器
c.begin(), c.end()   返回指向c的首元素和尾后位置的迭代器
c.cbegin(), c.cend() 返回const_iterator

2.迭代器

迭代器有着公共的接口:如果一个迭代器提供某个操作,那么所有迭代器提供相同的操作。 例如:解引用来访问容器的元素。

迭代器支持的所有操作如下表:

*iter                 返回迭代器iter所指元素的引用
iter->mem             获取该元素mem成员,等价于(*iter).mem
++iter                令iter指示容器的下一个元素
--iter                令iter指示容器的上一个元素
iter1 == iter2        判断两个迭代器是否相等,如果两个迭代器指示的是同一个元素
iter1 != iter2        ,或者它们是同一个容器的尾后迭代器,则相等;反之不等

①迭代器范围

一个迭代器范围由一对迭代器表示,两个迭代器分别指向同一个容器中的元素或者尾元素之后的位置。这两个迭代器通常被称为beginend(尾后迭代器)

这种元素范围被称为左闭合区间,即[begin,end)。begin和end必须指向相同的容器。begin可以与end指向相同的位置,但是不能指向begin之前的位置

②左闭合区间蕴含的编程假定

使用左闭合范围主要是因为这样有3个方便的性质:

  • 如果begin和end相等,则范围为空
  • 如果begin与end不等,则范围至少包含一个元素,且begin指向该范围的第一个元素
  • 我们可以对begin递增若干次,直到begin == end

如:用一个循环来处理一个元素范围,这是安全的

while (begin != end) {
	*begin = val;
	++begin;
}

相反,如果end指向最后一个元素,则当容器为空或者只有一个元素的情况下,begin和end都相等,这样就会出现很多麻烦。

3.容器定义和初始化

除了array之外,每个容器都定义了一个默认构造函数。 所有容器支持的定义和初始化操作如下表:

C c;              默认构造函数。如果C是一个array,则c中的元素按默认方式初始                     化,否则,c为空。
C c1(c2);         c1初始化为c2的拷贝。c1和c2必须是相同类型(容器相同,保存的
C c1 = c2;        元素也相同)
C c{a, b, c...}   c初始化为列表中的元素的拷贝。列表中元素的类型必须与c的元素类 
C c = {a, b...}   型相同,列表中元素数目必须等于或小于array的大小。
C c(b, e);        c初始化为迭代器b和e指定范围中的元素的拷贝。范围中元素的类型必                   须与c的元素类型相容
只有顺序容器(不包括array)的构造函数才接受大小参数
C seq(n)          seq包含n个元素,将这些元素进行值初始化
C seq(n, t)       seq包含n个初始化为值t的元素

①将一个容器初始化为另一个容器的拷贝

将一个新容器创建为另一个容器的拷贝的方法有两种:

  • 可以直接拷贝整个容器:要求两个容器及元素的类型必须相同。如:
list<string> s1{"abc", "def", "ghi"};
list<string> s2(s1); //正确:类型匹配
list<int> s3 = s1; //错误:容器类型不匹配
  • 拷贝由一个迭代器对指定的元素范围:两个容器不要求相同,且元素类型也可以不同,只要能将要拷贝的元素转换成初始化的容器的元素类型即可。如:
deque<char> d{'a', 'b', 'c'};
vector<int> v(d.begin(), d.end()); //正确:可以从char转换成int类型

②列表初始化

我们可以对容器进行列表初始化,这样做时就显式制定了容器中每个元素的值,并对array之外的容器类型,这样做还隐式制定了容器的大小(与初始值一样多的元素)。

list<string> s1{"abc", "def", "ghi"};
vector<const char*> s2{"abc", "def", "ghi"};

③与顺序容器大小相关的构造函数

除了array,顺序容器提供另一个构造函数,接受一个容器大小,和一个元素初始值(可忽略),如果未提供则将值初始化。如:

vector<int> v(10, -1); //10个int元素,每一个都初始化为-1
list<string> svec(10, "abc"); //10个元素都初始化为"abc"
deque<int> d(10); //10个元素,每个都初始化为0
deque<string> d2(10); //10个元素,每个都是空string(执行默认初始化)。

④标准库array具有固定大小

标准库类型array的大小也是类型的一部分,当定义一个array时,除了指定元素的类型,还要指定容器的大小:

array<int, 42>     //类型为:保存42个int的数组
array<string, 10>  //类型为:保存10个string的数组
array<int>         //错误:没有包括数组大小

array容器有如下初始化方式:

  • 默认初始化
array<int, 10> a1; //10个默认初始化的int
  • 列表初始化
array<int, 10> a2{0, 1, 2, 3}; //剩余元素为0
//等价于
array<int, 10> a2 = {0, 1, 2, 3};
  • 拷贝初始化:要求容器类型,元素类型,和容器大小一样
array<int, 10> a3 = a2;

4.赋值操作

所有容器都适用的赋值运算如下表:

c1 = c2;        将c1中的元素替换为c2中元素的拷贝。c1和c2必须具有相同的类型。
c = {a, b...};  将c1中元素替换为初始化列表中元素的拷贝(array不适用)
swap(c1, c2);   交换c1和c2中的元素。c1和c2必须具有相同的类型。swap通常比从c2 c1.swap(c2);    向c1拷贝快的多,如果c1不需要再使用,建议使用swap

只有除array之外的顺序容器才支持assign操作
seq.assign(b,e) 将seq中的元素替换为b和e所表示范围中的元素
seq.assign(i1)  将seq中的元素替换为初始化列表i1中的元素
seq.assign(n,t) 将seq中的元素替换为n个值为t的元素

直接拷贝赋值和列表赋值对应相应的初始化方式,其他初始化方式没有相应的“=”赋值方式,但是可以用assign来实现。 如:

vector<int> v1{1, 2, 3};
vector<char> v2{'a', 'b', 'c'};
deque<int> d;
d.assign(v2.begin(), v2.end()); //d的值为97 98 99
d.assign(10, 1); //d的值变成了10个1
d.assign({1, 2, 3}); //d的值又变成了1 2 3

5.swap

swap操作交换连个相同容器的内容。调用swap后,两个容器中的元素将交换,swap不对任何元素进行拷贝、删除或插入操作,只是交换了两个容器的内部数据结构(相当于给两个容器换了名字),因此可以保证在常数时间内完成。 如:

vector<string> v1(10);
vector<string> v2(24);
swap(v1, v2); //交换两个容器

需要注意的是,由于元素不会被移动,除string外,指向容器的**迭代器、引用和指针在swap操作之后都不会失效,仍指向swap操作之前所指向的那些元素。但是,在swap之后,这些元素已经属于不同的容器了。**如:

vector<string> v1(10);
vector<string> v2(24);
auto iter1 = v1.begin() + 1; //指向v1的第2个string元素
swap(v1, v2); //交换两个容器
//iter1仍指向原来v1中第二个string元素,但是交换过后,变成了v2的第二个string

容器及提供成员函数版本的swap,也提供非成员函数版本的swap。非成员版本的swap是非常重要的,统一使用非成员函数版本的swap是一个好习惯。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值