C++泛型笔记
容器操作
类型别名
—— | —— |
---|---|
iterator | 此容器类型的迭代器类型 |
const_iterator | 可以读取元素但不能修改元素的迭代器类型 |
size_type | 无符号整数类型,足够保存此种容器类型最大可能容器的大小 |
difference_type | 带符号整数类型足够保存两个迭代器之间的距离 |
vlaue_type | 元素类型 |
reference | 元素左值类型;与vlaue_type& 含义相同 |
constreference | 元素的const 左值类型(即,const value_type& ) |
构造函数
—— | —— |
---|---|
C c; | 默认构造函数,构造空容器(array P301) |
C c1(c2); | 构造c2 的拷贝c1 ,c1 初始化为c2 的拷贝。c1 和c2 必须是相同的类型(即它们必须是相同的容器类型,且保存的是相同的元素类型; 对于 arary 类型两者还必须具有相同的大小) |
C c(b,e); | 构造c ,将迭代器b 和e 指定的范围内的元素拷贝到c (array 不支持)范围内的元素类型必须与C的元素类型相容,而容器类型不必相同 |
C c{a,b,c
…
} C = {a,b,c
…
} | 列表初始化c ,列表中的类型必须与C 中的元素类型相容(即列表中的元素类型可以转化为容器中的元素类型) |
只有顺序容器(不包括array
)
的构造函数才能接受大小参数
C seq(n)
|seq
包括n
个元素,这些元素进行了值初始化;
此构造函数是explicit
的(string
不适用)
C seq(n,t)
|seq
包含n
个初始化为值t
的元素
赋值与swap
—— | —— |
---|---|
c1=c2 | 将c1 中的元素替换为c2 中的元素,c1 和c2 必须有相同的类型 |
c1 = {a,b,c
…
} | 将c1 中的元素替换为列表中的元素(不适用于array ) |
a.swap(b) | 交换a 和b 的元素,c1 和c2 必须有相同的类型,大小不必相同 |
swap(a,b) | 与a.swap(b) 等价 |
assign
不适用于关联容器和array
seq.assign(il)
|将seq
中的元素替换为初始化列表il
中的元素
seq.assign(n,t)
|将seq
中的元素替换为n
个值为t
的元素
注意:
assign
相当于先对容器进行clear
操作,然后再对容器进行添加元素的操作assign
相关操作会导致左边容器内部的迭代器、引用和指针失效。swap
交换两个具有相同类型的容器的内容,除array
外,swap
只是交换了两个容器的数据结构,元素本身并未交换,而且指向容器的迭代器、指针、引用在`swap
后不会失效。它们仍指向swap
操作之前所指向的那些元素。但是,在swap
之后,这些元素已经属于不同的容器。swap array
会真正交换它们的元素,也就是进行了复制操作,所以原来的指针、引用和迭代器绑定的元素保持不变。但是元素值已经和另一个array
中对应元素的值进行了交换。- 对
string
来说,调用swap
会导致迭代器、引用和指针失效。
容器大小
—— | —— |
---|---|
c.size() | c 中元素的数目(不支持forward_list ) |
c.max_size() | c 可保存的最大元素数目 |
c.empty() | 若c 中存储了元素,返回false ,否则返回true |
添加/删除元素(不适用与array
)
—— | —— |
---|---|
c.insert(args) | 将args 中的元素拷贝进c |
c.emplace(inits) | 使用inits 构造c 中的一个元素 |
c.erase(args) | 删除args 指定的元素 |
c.clear() | 删除c 中的所有元素,返回void |
注:不同容器中这些操作的接口都不同
向顺序容器中添加元素
—— | —— |
---|---|
c.push_back(t) c.emplace_back(args) | 在c 的尾部创建一个值为t 或由args 创建的元素。返回void |
c.push_front(t) c.emplace_front(args) | 在c 的头部创建一个值为t 或由args 创建的元素。返回void |
c.insert(p,t) c.emplace(p,args) | 在迭代器p 指向的元素之前创建一个值为t 或由args 创建的元素。返回指向新添加元素的迭代器 |
c.insert(p,n,t) | 在迭代器p 指向的元素之前插入n 个值为t 的元素。返回指向新添加的第一个元素的迭代器;若 n 为0,则返回p |
c.insert(p,b,e) | 将迭代器b 和e 指定的范围内的元素插入到迭代器p 指向的元素之前。b 和e 不能指向 c 中的元素。返回指向新添加的第一个元素的迭代器;若范围为空,则返回p |
c.insert(p,il) | il 是一个花括号包围的元素值列表。将这些给定值插入到迭代器p 指向的元素之前。返回指向新添加的第一个元素的迭代器;若范围为空,则返回 p |
顺序容器的删除操作
—— | —— |
---|---|
c.pop_back() | 删除c 中的尾元素。若c 为空,则函数行为未定义。函数返回void |
c.pop_front() | 删除c 中的首元素。若c 为空,则函数行为未定义。函数返回void |
c.erase(p) | 删除迭代器p 所指的元素,返回一个指向被删元素之后元素的迭代器,若p 指向尾元素,则返回尾后( off-the-end )迭代器。若p 是尾后迭代器,则函数行为未定义 |
c.earse(b,e) | 删除迭代器b 和e 所指定范围内的元素。返回一个指向最后一个被删元素之后元素的迭代器,若 e 本身就是尾后迭代器,则函数也返回尾后迭代器。 |
c.clear() | 删除c 中的所有元素。返回void |
注意:
- 以上顺序容器的添加和删除操作都会改变容器的大小,所以不适用与
array
。 forward_list
有自己专有版本的insert
、emplace
、erase
(P312),forward_list
不支持push_back
、empalce_back
和pop_back
vector
和string
不支持push_front
、emplace_front
和pop_front
- 向一个
vector
、string
插入元素,会使所有指向容器插入位置后的迭代器、引用、指针失效,删除类似,删除点之后位置的迭代器、引用和指针都会失效。;deque
中删除或者插入除首尾位置之外的任何元素都会使所有迭代器、引用和指针失效。对于list
和forward_list
,指向容器的迭代器(包括尾后迭代器和首前迭代器)、指针和引用仍有效。
所以我们一般保存尾后迭代器:
auto begin = v.begin(),end = v.end();
while(begin != end){ //由于insert使得end的值已经改变
++begin;
begin = v.insert(begin,42); //begin后的迭代器都会失效,包括尾后迭代器
++begin;
}
- 虽然
vector
和string
都不支持push_front
操作,但是他们对于insert
操作并无类似限制(插入开始位置),但是这很耗时。
vector<string> svec;
svec.insert(svec.begin(),"Hello!")
- 使用
emplace操作
//在c的末尾构造一个Sales_data,调用了Sales_data对应3个参数的构造函数
c.emplace_back("987-0590",25,15.99);
//错误:没有接受3个参数的push_back版本
c.push_back("978-0590",25,15.99);
//正确:创建一个临时的Scales_data对象传递给push_back,但是相比2来说,3多调用了一次复制构造函数
c.push_back(Sales_data("978-0590",25,15.99));
emplace
、emplace_back
、emplace_front
都适用于向容器中添加自定义的对象。
emplace
和insert
都是在迭代器指向元素之前插入或创建元素,返回最新添加的元素,以便下一次在新添加的元素前继续添加。而erase
是删除迭代器指定元素或范围内的元素,返回最后一个被删除元素之后元素的迭代器。以便下一次继续删除。
顺序容器中访问元素操作
—— | —— |
---|---|
c.back() | 返回c 中尾元素的引用。若c 为空,函数行为未定义 |
c.front() | 返回c 中首元素的引用。若c 为空,函数行为未定义 |
c.[n] | 返回c 中下标为n 的元素的引用,n 是一个无符号整数。若n>c.size() ,则函数行为未定义 |
c.at() | 返回c 中下标为n 的元素的引用,如果下标越界,则抛出一out_of_range 异常 |
- 对空容器中元素访问的行为(通过迭代器或者上面的方式),就相当于下标越界,所以访问前要用
empty
验证容器是否为空。 c.back()
、c.front()
、c.[n]
返回的都是引用,若容器是const
的返回引用也是const
,若容器不是const
,那么返回的是普通引用,可以通过它改变容器中元素的值。如:
c.front() = 42;
关系运算符
—— | —— |
---|---|
==,!= | 所有容器都支持相等(不等)运算符 |
<,<=,>,>= | 关系运算符(无序关联容器不支持) |
获取迭代器
—— | —— |
---|---|
c.begin(),c.end() | 返回指向c 的首元素和尾元素之后位置的迭代器 |
c.cbegin(),c.cend() | 返回const_iterator |
反向容器的额外成员(不支持forward_list
)
—— | —— |
---|---|
reverse_iterator | 按逆序寻址元素的迭代器 |
const_reverse_iterator | 不能修改元素的逆序迭代器 |
c.rbegin(),c.rend() | 返回指向c 的尾元素和首元素之前位置的迭代器 |
c.crbegin(),c.crend() | 返回const_reverse_iterator |
迭代器范围
它由一对迭代器begin
和end
表示,必须满足以下条件:
+ 它们指向同一容器中的元素,或者是最后一个元素之后的位置。
+ 我们可以通过反复递增begin
来达到end
。换句话说,end
不能在begin
之前
注意:迭代器范围是一个左闭右开的区间
泛型算法
accumulate
定义在头文件numeric
中
入口参数:
accumulate(begin,end,init)
begin
和end
表示一个迭代器范围,init
表示累加的初始值,容器中元素类型必须支持+运算符
equal
入口参数:
equal(v1.begin(),v1.end(),v2.begin())
v1.begin()
、v1.end()
表示容器v1
的迭代范围,v2.begin()
表示从容器v2
开始进行比较,v2
的长度必须大于v1
的长度,容器类型以及容器中元素类型都不必一样。
fill
入口参数:
fill(begin,end,value)
begin
和end
表示一个迭代器范围,value
表示写入序列的数据的值
fill(v.begin(),v.end(),0);
fill(v.begin(),v.begin()+v.size()/2,10);
fill_n
fill(begin,num,value)
begin
是迭代器,表示开始写入元素的位置,num
表示写入元素的个数,value
表示写入的值。从begin
开始至少有num
个元素。
vector<int> v;
fill_n(v.begin(),v.size(),0); //所有元素重置为0
vector<int> v1; //空向量
fill_n(v1.begin(),10,0); // v1中并没有这么大空间,造成越界
插入迭代器
是一种迭代器适配器,接受一个容器,生成一个迭代器。该迭代器调用容器操作来向给定容器的指定位置插入一个元素。
插入迭代器的操作
—— | —— |
---|---|
it = t | 在指定的当前位置插入值t 。假定c 是it 绑定的容器,依赖于插入迭代器的不同种类,此赋值分别会调用 c.push_back(t) 、c.push_front(t) 、c.insert(t,p) ,其中p 为传递给inserter 的迭代器位置。 |
插入迭代器的种类
—— | —— |
---|---|
back_inserter | 创建一个使用push_back 的迭代器 |
front_inserter | 创建一个使用push_front 的迭代器 |
inserter | 创建一个使用inserter 的迭代器。此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器。元素将被插入到给定迭代器所表示的元素之前。 |
注意:
- 只有在容器支持
push_back
、push_front
的情况下,才可能使用back_inserter
、front_inserter
。 front_inserter
和inserter
的区别
// it是由inserter生成的迭代器
*it = val;
// 与下面代码等价
it = c.insert(it,val); // it指向新加入的元素
++it; //递增it,使它指向原来的元素
list<int> lst = {1,2,3,4};
list<int> lst2,lst3; // 空list
// 拷贝完后,lst2包含 4,3,2,1,是逆序的
copy(lst.cbegin(),lst.cend(),front_inserter(lst2)); // 由于front_inserter返回的迭代器,每次插入后,
//迭代器都指向首元素,而不是递增,指向原来的元素
// 拷贝完后,lst2包含 1,2,3,4
copy(lst.cbegin(),lst.cend(),inserter(lst3,lst3.begin()));
vecot<int> v; //空向量
//通过back_insert返回的迭代器执行的是插入操作,不用考虑越界问题,而且每次插入元素后,
//迭代器会自动递增,指向新加入的元素,而且插入的顺序是正序的,这与inserter是相同的。
fill_n(back_insert(v),10,0); //如果第一个参数为v,会出现越界问题
- 插入器常常与泛型算法,如:
fill_n
、copy
,联用,避免for循环,代码更加简洁。