【C++】第八章 STL

本文详细介绍了C++ STL(标准模板库),包括容器如string、array、valarray、vector、deque、stack、queue、list、set、map等的特性和使用方法。此外,还涵盖了STL算法如查找、排序、通用、删除替换等,以及迭代器和适配器的概念。STL是C++标准库的一部分,提供了高效、可重用的代码,提高了程序的性能和移植性。

该文章内容整理自《C++ Primer Plus(第6版)》、《Effective C++(第三版)》、以及网上各大博客

STL 是“Standard Template Library”的缩写,意为“标准模板库”,它是 C++ 标准库的一部分,借助模板把常用的数据结构及其算法都实现了一遍。STL 从广义上分为: 容器(container)、算法(algorithm)、以及迭代器(iterator)。容器和算法之间通过迭代器进行无缝连接

STL提供了六大组件,彼此之间可以组合套用

  1. 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据,从实现角度来看,STL容器是一种class template
  2. 算法:各种常用的算法,如sort、find、copy、for_each。从实现的角度来看,STL算法是一种function template
  3. 迭代器:扮演了容器与算法之间的胶合剂,共有五种类型,从实现角度来看,迭代器是一种将operator* 、operator-> 、operator++、operator–等指针相关操作予以重载的class template。所有STL容器都附带有自己专属的迭代器,只有容器的设计者才知道如何遍历自己的元素。原生指针(native pointer)也是一种迭代器
  4. 仿函数:行为类似函数,可作为算法的某种策略。从实现角度来看,仿函数是一种重载了operator()的class 或者class template。仿函数可将对函数的修改都封装到类里面,通过类方法来进行
struct MyPlus{
    int operator()(const int &a , const int &b) const {
        return a + b;
    }
} a;

MyPlus()(1,2);	//通过产生临时对象调用重载运算符
a.operator()(1,2);	//通过对象显示调用重载运算符
a(1,2);		//通过对象类似函数调用 隐示地调用重载运算符
  1. 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西
  2. 空间配置器:负责空间的配置与管理。从实现角度看,配置器是一个实现了动态空间配置、空间管理、空间释放的class tempalte
    STL六大组件的交互关系,容器通过空间配置器取得数据存储空间,算法通过迭代器存储容器中的内容,仿函数可以协助算法完成不同的策略的变化,适配器可以修饰仿函数

STL广义上的三大组件

  1. 容器
    几乎任何特定的数据结构都是为了实现某种特定的算法。STL容器就是将运用最广泛的一些数据结构实现出来。常用的数据结构:数组(array)、链表(list)、tree(树)、栈(stack)、队列(queue)、集合(set)、映射表(map),根据数据在容器中的排列特性,这些数据分为序列式容器和关联式容器两种
    (1). 序列式容器强调值的排序,序列式容器中的每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。有Vector容器、Deque容器、List容器等
    (2). 关联式容器是非线性的树结构,更准确的说是二叉树结构。各元素之间没有严格的物理上的顺序关系,也就是说元素在容器中并没有保存元素置入容器时的逻辑顺序。关联式容器另一个显著特点是在值中选择一个值作为关键字key,这个关键字对值起到索引的作用,方便查找。有Set容器、multiset容器、Map容器、multimap容器等
  2. 算法
    算法是指问题的解法,以有限的步骤解决逻辑或数学上的问题。STL收录的算法经过了数学上的效能分析与证明,是极具复用价值的,包括常用的排序,查找等等。特定的算法往往搭配特定的数据结构,算法与数据结构相辅相成。算法可分为质变算法和非质变算法
    (1). 质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等
    (2). 非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等
  3. 迭代器
    迭代器(iterator)是一种抽象的设计概念,现实程序语言中并没有直接对应于这个概念的实物。iterator模式定义如下:提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。迭代器的设计思维是STL的关键所在,STL的中心思想在于将容器(container)和算法(algorithms)分开,彼此独立设计,最后再一贴胶着剂将他们撮合在一起。从技术角度来看,容器和算法的泛型化并不困难,C++的class template和function template可分别达到目标,如果设计出两这个之间的良好的胶着剂,才是大难题。迭代器的种类如下
    iterator

STL优点

  • STL 是 C++的一部分,因此不用额外安装什么,它被内建在编译器之内
  • STL 的一个重要特性是将数据和操作分离。数据由容器类别加以管理,操作则由可定制的算法定义。迭代器在两者之间充当“粘合剂”,以使算法可以和容器交互运作
  • 程序员可以不用思考 STL 具体的实现过程,只要能够熟练使用 STL 就可以了
  • STL 具有高可重用性,高性能,高移植性,跨平台的优点
    (1). 高可重用性:STL 中几乎所有的代码都采用了模板类和模版函数的方式实现,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会
    (2). 高性能:如 map 可以高效地从十万条记录里面查找出指定的记录,因为 map 是采用红黑树的变体实现的
    (3). 高移植性:如在项目 A 上用 STL 编写的模块,可以直接移植到项目 B 上

常见容器

string

C++98提供了string类,被包含在头文件string中。string类是STL中basic_string模板实例化得到的模板类。string 类的成员函数有很多,同一个名字的函数也常会有五六个重载的版本。这里仅对常用成员函数按功能进行分类并介绍。为方便起见,子串(n, m)是指从下标开始的m个字符组成的子串

  1. 初始化
    string 类有多个构造函数,用法示例如下。另外,string类同样可以使用列表初始化
string s1();  		// s1 = ""
string s2("Hello"); // s2 = "Hello"
string s3(4, 'K');  // s3 = "KKKK"
string s4("12345", 1, 3);  //s4 = "234",即 "12345" 的从下标1开始,长度为3的子串

//string类不能用整数或字符作为参数,下面两种方法错误
string s5('K');
string s6(123);
  1. 赋值
    可以用字符串常量, char* 类型的变量、常量,以及 char 类型的变量、常量对 string 对象进行直接赋值。string 类还有 assign 成员函数,可以用来对 string 对象赋值。assign 成员函数返回对象自身的引用
string s1;
s1 = "Hello";  // s1 = "Hello"
s2 = 'K';  // s2 = "K”

string s1("12345"), s2;
s3.assign(s1);  // s3 = s1
s2.assign(s1, 1, 2);  // s2 = "23",即 s1 的子串(1, 2)
s2.assign("abcde", 2, 3);  // s2 = "cde"
s2.assign(4, 'K');  // s2 = "KKKK"
  1. 长度
    在string类中,length 成员函数与size 成员函数功能完全一样,都返回字符串的长度
  2. 连接
    除了可以使用+和+=运算符对 string 对象执行字符串的连接操作外,string 类还有 append 成员函数,可以用来向字符串后面添加内容。append 成员函数返回对象自身的引用
string s1("123"), s2("abc");
s1.append(s2);  // s1 = "123abc"
s1.append(s2, 1, 2);  // s1 = "123abcbc"
s1.append("ABCDE", 2, 3);  // s1 = "123abcbcKKKCDE"
s1.append(3, 'K');  // s1 = "123abcbcKKK"
  1. 比较
    除了可以用 <、<=、==、!=、>=、> 运算符比较 string 对象外,string 类还有 compare 成员函数,可用于比较字符串。另外,==操作符是重载调用了compare函数,因而两种方法结果一样,但是效率不同
string s1("hello"), s2("hello, world");
int n = s1.compare(s2);
n = s1.compare(1, 2, s2, 0, 3);  //比较s1的子串 (1,2) 和s2的子串 (0,3)
n = s1.compare(0, 2, s2);  // 比较s1的子串 (0,2) 和 s2
n = s1.compare("Hello");
n = s1.compare(1, 2, "Hello");  //比较 s1 的子串(1,2)和"Hello”
n = s1.compare(1, 2, "Hello", 1, 2);  //比较 s1 的子串(1,2)和 "Hello" 的子串(1,2)
  1. 子串
    substr 成员函数可以用于求子串 (n, m)
string s1 = "this is ok";
string s2 = s1.substr(2, 4);  // s2 = "is i"
s2 = s1.substr(2);  // s2 = "is is ok"
  1. 交换
    swap 成员函数可以交换两个 string 对象的内容
string s1("West”), s2("East");
s1.swap(s2);  // s1 = "East",s2 = "West"
  1. 查找
    string 类有一些查找子串和字符的成员函数,它们的返回值都是子串或字符在 string 对象字符串中的位置。如果查不到,则返回 string::npos。string::npos 是在 string 类中定义的一个静态常量。这些函数如下:
    find:从前往后查找子串或字符出现的位置
    rfind:从后往前查找子串或字符出现的位置
    find_first_of:从前往后查找何处出现另一个字符串中包含的字符
    find_last_of:从后往前查找何处出现另一个字符串中包含的字符
    find_first_not_of:从前往后查找何处出现另一个字符串中没有包含的字符
    find_last_not_of:从后往前查找何处出现另一个字符串中没有包含的字符
string s1("Source Code");
int n;
if((n = s1.find('u')) != string::npos)//查找 u 出现的位置
if((n = s1.find("Source", 3)) == string::npos)//从下标3开始查找 u 出现的位置
if((n = s1.find("Co")) != string::npos)//查找子串"Co"的位置
if((n = s1.find_first_of("ceo")) != string::npos)//查找第一次出现或 'c'、'e'或'o'的位置
if((n = s1.find_last_of('e')) != string::npos)//查找最后一个 'e' 的位置
if((n = s1.find_first_not_of("eou", 1)) != string::npos) //从下标1开始查找第一次出现非 'e'、'o' 或 'u' 字符的位置
  1. 替换子串
    replace 成员函数可以对 string 对象中的子串进行替换,返回值为对象自身的引用
string s1("Real Steel");
s1.replace(1, 3, "123456", 2, 4);  //用 "123456" 的子串(2,4) 替换 s1 的子串(1,3)
s1.replace(2, 3, 5, '0');  //用 5 个 '0' 替换子串(2,3)
s1.replace(2, 5, "XXX");  //将子串(n,5)替换为"XXX"
  1. 删除子串
    erase 成员函数可以删除 string 对象中的子串,返回值为对象自身的引用
string s1("Real Steel");
s1.erase(1, 3);  //删除子串(1, 3),此后 s1 = "R Steel"
s1.erase(5);  //删除下标5及其后面的所有字符,此后 s1 = "R Ste"
  1. 插入字符串
    insert 成员函数可以在 string 对象中插入另一个字符串,返回值为对象自身的引用
string s1("Limitless"), s2("00");
s1.insert(2, "123");  //在下标 2 处插入字符串"123",s1 = "Li123mitless"
s1.insert(3, s2);  //在下标 2 处插入 s2 , s1 = "Li10023mitless"
s1.insert(3, 5, 'X');  //在下标 3 处插入 5 个 'X',s1 = "Li1XXXXX0023mitless"
  1. 将 string 对象作为流处理
    使用流对象 istringstream 和 ostringstream,可以将 string 对象当作一个流进行输入输出。使用这两个类需要包含头文件 sstream
string src("Avatar 123 5.2 Titanic K");
istringstream istrStream(src); //建立src到istrStream的联系
string s1, s2;
int n;  double d;  char c;
istrStream >> s1 >> n >> d >> s2 >> c; //把src的内容当做输入流进行读取
ostringstream ostrStream;
ostrStream << s1 << endl << s2 << endl << n << endl << d << endl << c <<endl;
cout << ostrStream.str();
/*
程序的输出结果是:
Avatar
Titanic
123
5.2
K
*/
  1. 用 STL 算法操作 string 对象
    string 对象也可以看作一个顺序容器,它支持随机访问迭代器,也有 begin 和 end 等成员函数。STL 中的许多算法也适用于 string 对象
string s("afgcbed");
string::iterator p = find(s.begin(), s.end(), 'c');
if(p!= s.end()) cout << p - s.begin() << endl;  //输出 3
sort(s.begin(), s.end());
cout << s << endl;  //输出 abcdefg
next_permutation(s.begin(), s.end());
cout << s << endl; //输出 abcdegf

关于CString、string、string.h三个头文件:

  • CString:CString是MFC或者ATL中的实现,是MFC里面封装的一个关于字符串处理的功能很强大的类,只有支持MFC的工程才能使用。如在linux上的工程就不能用CString,只能使用标准C++中的string类。在MFC中使用不需要自己加,但在另外的程序中需要加入#include<CString>
  • string:string为标准模板类(STL)定义的字符串类,已纳入C++标准中。另外因为string类是在标准C++库中,使用之前需要声明名称空间std;而CString类并不在std命名空间中,因为它并不是c++标准库,只是一个微软的封装库。所以string类的程序移植性更好。同时,string类与CString类的方法接口也不太一样。一般我们使用cout重载string类,而不是CString类
  • string.h:c语言中关于字符数组的函数定义的头文件,常用函数有strlen,strcpy,strcmp等,与string类无半点关系,所以头文件<string>与<string.h>无关

array

C++中的数组类型是继承了C语言的特性,在使用数组的时候要注意数组越界操作问题。为了更安全的对数组进行操作,C++提出了数组模板类array。array是具有固定大小的数组。支持快速随机访问,不能添加或删除元素。使用时需要包含头的头文件<array>

  1. 声明:注意在array类的声明中还有一个定义数组大小的参数
    如array<int, 5> arr = {1, 2, 3, 4, 5};
  2. 容量:array类的容量可用sizeof()、size()、max_size()、empty()等函数进行检测
  3. 访问:下标[ ]、at()、front()、back()、data()等函数访问array对象内的元素
  4. 修改:可以使用fill()、swap()等函数对array对象整体进行操作。fill()函数能将数组全部元素的值修改,而swap()函数能整体地交换两个数组的全部值而不是交换数组内部的两个元素
  5. 比较:可用>、<、==等符号对两个array对象进行比较

valarray

valarray类面向一系列数值高速计算的数组,在C++11中才支持。使用时需要头文件<valarray>
valarray类与vector的主要区别在于以下两点:

  1. valarray定义了一组在两个相同长度和相同类型的valarray类对象之间的数字计算,例如xarr = cos(yarr) + sin(zarr)
  2. 通过重载operater[],可以返回valarray的相关信息(valarray其中某个元素的引用、特定下标的值或者其某个子集)

常用函数:

  1. apply。将 valarray 数组的每一个值都用 apply 所接受到的函数进行计算
  2. cshift。将 valarray 数组的数据进行循环移动,参数为正者左移为负就右移
  3. max。返回 valarray 数组的最大值
  4. min。返回 valarray 数组的最小值
  5. resize。重新设置 valarray 数组大小,并对其进行初始化
  6. shift。将 valarray 数组移动,参数为正者左移,为负者右移,移动后由 0 填充剩余位
  7. size。得到数组的大小
  8. sum。数组求和

公共函数(对数组的操作):

  1. abs。对数组的每一个元素取绝对值
  2. acos。返回每个元素的反余弦值
  3. asin。 返回每个元素的反正弦值
  4. atan。 返回每个元素的正切值
  5. atan2。笛卡尔正切值
  6. cos。余弦值
  7. cosh。 双曲线余弦值
  8. exp。返回自然指数E^x
  9. log。返回自然对数
  10. log10。返回以10 为底的返回自然对数
  11. exp。返回x^y
  12. sin。正弦值
  13. sinh。双曲线正弦值
  14. sqrt。开方
  15. tan。正切值
  16. tanh。反正切值

构造函数:

  1. valarray( );
  2. explicit valarray( size_t Count );
  3. valarray( const Type& Val , size_t Count );
  4. valarray( const Type* Ptr , size_t Count );
  5. valarray( const slice array<Type>& SliceArray );
  6. valarray( const gslice array<Type>& GsliceArray );
  7. valarray( const mask array<Type>& MaskArray );
  8. valarray( const indirect array<Type>& IndArray );

slice 类用法:
该类主要配合 valarray 类使用,可以从 valarray 中提取子数组

//构造函数
slice();
slice( 
	size_t StartIndex,	// 截取数组的开始位置
	const valarray<size_t> Len, 	// 子数组的最大长度
	const valarray<size_t> Stride	// 相隔多少个元素选中一个
);

//例子
valarray<int> va(20), vaResult;
for(int i = 0; i < 20 ; ++i) va[i] = 2 * (i + 1);
slice vaSlice(1, 7, 3);
vaResult = va[vaSlice];

Gslice 类用法:
Gslice 类的用法和slice 基本相同,只是它截取的是循环子串,当母串进行一次提取后的字串元素数目达不到要求时,gslice 会将提取后的母串继续组合进行提取直到满足要求或者母串被提取完了

vector

vector的数据安排以及操作方式与array非常相似,两者的唯一差别在于空间的运用的灵活性:

  • Array是静态空间,一旦配置了就不能改变,要换大一点或者小一点的空间,可以,一切琐碎得由自己来,首先配置一块新的空间,然后将旧空间的数据搬往新空间,再释放原来的空间
  • Vector是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新元素。因此vector的运用对于内存的合理利用与运用的灵活性有很大的帮助

实现原理:
Vector所采用的数据结构非常简单,线性连续空间。它以两个迭代器_Myfirst和_Mylast分别指向配置得来的连续空间中目前已被使用的范围,并以迭代器_Myend指向整块连续内存空间的尾端
为了降低空间配置时的速度成本,vector实际配置的大小可能比客户端需求大一些,以备将来可能的扩充,这边是容量的概念。换句话说,一个vector的容量永远大于或等于其大小,一旦容量等于大小,便是满载,下次再有新增元素,整个vector容器就得另觅居所
所谓动态增加大小,并不是在原空间之后续接新空间(因为无法保证原空间之后尚有可配置的空间),而是一块更大的内存空间,然后将原数据拷贝新空间,并释放原空间。因此,对vector的任何操作,一旦引起空间的重新配置,指向原vector的所有迭代器就都失效了

使用:

  1. 构造函数:
vector<T> v; //采用模板实现类实现,默认构造函数
vector(v.begin(), v.end());//将v[begin(), end())区间中的元素拷贝给本身。
vector(n, elem);//构造函数将n个elem拷贝给本身。
vector(const vector &vec);//拷贝构造函数。
  1. 赋值:
assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem);//将n个elem拷贝赋值给本身。
vector& operator=(const vector &vec);//重载等号操作符
swap(vec);// 将vec与本身的元素互换。
  1. 容量:
size();//返回容器中元素的个数
empty();//判断容器是否为空
resize(int num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
resize(int num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长>度的元素被删除。
capacity();//容器的容量
reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问。
  1. 存取:
at(int idx); //返回索引idx所指的数据,如果idx越界,抛出out_of_range异常。
operator[];//返回索引idx所指的数据,越界时,运行直接报错
front();//返回容器中第一个数据元素
back();//返回容器中最后一个数据元素
  1. 插入删除:
push_back(ele); //尾部插入元素ele
pop_back();//删除最后一个元素
insert(const_iterator pos, int count,ele);//迭代器指向位置pos插入count个元素ele
erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素
erase(const_iterator pos);//删除迭代器指向的元素
clear();//删除容器中所有元素

deque

vector容器是单向开口的连续内存空间,deque则是一种双向开口的连续线性空间。所谓的双向开口,意思是可以在头尾两端分别做元素的插入和删除操作,当然,vector容器也可以在头尾两端插入元素,但是在其头部操作效率奇差,无法被接受
Deque容器和vector容器最大的差异,一在于deque允许使用常数项时间对头端进行元素的插入和删除操作。二在于deque没有容量的概念,因为它是动态的以分段连续空间组合而成,随时可以增加一段新的空间并链接起来,换句话说,像vector那样,”旧空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间”这样的事情在deque身上是不会发生的。也因此,deque没有必须要提供所谓的空间保留(reserve)功能.
虽然deque容器也提供了Random Access Iterator,但是它的迭代器并不是普通的指针,其复杂度和vector不是一个量级,这当然影响各个运算的层面。因此,除非有必要,我们应该尽可能的使用vector,而不是deque。对deque进行的排序操作,为了最高效率,可将deque先完整的复制到一个vector中,对vector容器进行排序,再复制回deque

实现原理:
Deque容器是连续的空间,至少逻辑上看来如此,连续现行空间总是令我们联想到array和vector。array无法成长,vector虽可成长,却只能向尾端成长(但其成长其实是一个假象,事实上经过了申请更大空间、原数据复制新空间、释放原空间三步骤,如果不是vector每次配置新的空间时都留有余裕,其成长假象所带来的代价是非常昂贵的)
Deque是由一段一段的定量的连续空间构成。一旦有必要在deque前端或者尾端增加新的空间,便配置一段连续定量的空间,串接在deque的头端或者尾端。Deque最大的工作就是维护这些分段连续的内存空间的整体性的假象,并提供随机存取的接口,避开了重新配置空间,复制,释放的轮回,代价就是复杂的迭代器架构
既然deque是分段连续内存空间,那么就必须有中央控制,维持整体连续的假象,数据结构的设计及迭代器的前进后退操作颇为繁琐。Deque代码的实现远比vector或list都多得多
Deque采取一块所谓的map(不是STL的map容器)作为主控,这里所谓的map是一小块连续的内存空间,其中每一个元素都是一个指针,指向另一段连续性内存空间,称作缓冲区。缓冲区才是deque的存储空间的主体

使用:

  1. 构造函数:
deque<T> deqT;//默认构造形式
deque(beg, end);//构造函数将[beg, end)区间中的元素拷贝给本身。
deque(n, elem);//构造函数将n个elem拷贝给本身。
deque(const deque &deq);//拷贝构造函数。
  1. 赋值:
assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem);//将n个elem拷贝赋值给本身。
deque& operator=(const deque &deq); //重载等号操作符 
swap(deq);// 将deq与本身的元素互换
  1. 容量:
deque.size();//返回容器中元素的个数
deque.empty();//判断容器是否为空
deque.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除
deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除
  1. 插入删除:
push_back(elem);//在容器尾部添加一个数据
push_front(elem);//在容器头部插入一个数据
pop_back();//删除容器最后一个数据
pop_front();//删除容器第一个数据
insert(pos,elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置
insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值
insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值
erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置
erase(pos);//删除pos位置的数据,返回下一个数据的位置
clear();//移除容器的所有数据
  1. 存取:
at(idx);//返回索引idx所指的数据,如果idx越界,抛出out_of_range。
operator[];//返回索引idx所指的数据,如果idx越界,不抛出异常,直接出错。
front();//返回第一个数据
back();//返回最后一个数据

stack

stack是一种先进后出的数据结构,它只有一个出入口。stack容器允许新增元素,移除元素,取得栈顶元素。但是除了最顶端外,没有任何其他方法可以存取stack的其他元素。换言之,stack不允许有遍历行为,也不提供迭代器

使用:

  1. 构造函数:
stack<T> stkT;//stack采用模板类实现, stack对象的默认构造形式: 
stack(const stack &stk);//拷贝构造函数
  1. 赋值:
stack& operator=(const stack &stk);//重载等号操作符
  1. 存取:
push(elem);//向栈顶添加元素
pop();//从栈顶移除第一个元素
top();//返回栈顶元素
  1. 容量:
empty();//判断堆栈是否为空
size();//返回堆栈的大小

queue

Queue是一种先进先出的数据结构,它有两个出入口,queue容器允许从一端新增元素,从另一端移除元素。queue不提供遍历功能,也不提供迭代器

使用:

  1. 构造函数:
queue<T> queT;//queue采用模板类实现,queue对象的默认构造形式:
queue(const queue &que);//拷贝构造函数
  1. 存取、插入、删除:
push(elem);//往队尾添加元素
pop();//从队头移除第一个元素
back();//返回最后一个元素
front();//返回第一个元素
  1. 赋值:
queue& operator=(const queue &que);//重载等号操作符
  1. 容量:
empty();//判断队列是否为空
size();//返回队列的大小

list

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
list和vector是两个最常被使用的容器。相较于vector的连续线性空间,list就显得负责许多,它的好处是每次插入或者删除一个元素,就是配置或者释放一个元素的空间。因此,list对于空间的运用有绝对的精准,一点也不浪费。而且,对于任何位置的元素插入或元素的移除,list永远是常数时间
list容器是一个循环双向链表:

  • 采用动态存储分配,不会造成内存浪费和溢出
  • 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
  • 链表灵活,但是空间和时间额外耗费较大

迭代器:
list容器不能像vector一样以普通指针作为迭代器,因为其节点不能保证在同一块连续的内存空间上。list迭代器必须有能力指向list的节点,并有能力进行正确的递增、递减、取值、成员存取操作。所谓“list正确的递增,递减、取值、成员取用”是指,递增时指向下一个节点,递减时指向上一个节点,取值时取的是节点的数据值,成员取用时取的是节点的成员。
由于list是一个双向链表,迭代器必须能够具备前移、后移的能力,所以list容器提供的是Bidirectional Iterators。
list有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效。这在vector是不成立的,因为vector的插入操作可能造成记忆体重新配置,导致原有的迭代器全部失效,甚至List元素的删除,也只有被删除的那个元素的迭代器失效,其他迭代器不受任何影响

使用:

  1. 构造函数:
list<T> lstT;//list采用采用模板类实现,对象的默认构造形式:
list(beg,end);//构造函数将[beg, end)区间中的元素拷贝给本身。
list(n,elem);//构造函数将n个elem拷贝给本身。
list(const list &lst);//拷贝构造函数。
  1. 插入删除:
push_back(elem);//在容器尾部加入一个元素
pop_back();//删除容器中最后一个元素
push_front(elem);//在容器开头插入一个元素
pop_front();//从容器开头移除第一个元素
insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置
insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值
insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值
clear();//移除容器的所有数据
erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置
erase(pos);//删除pos位置的数据,返回下一个数据的位置
remove(elem);//删除容器中所有与elem值匹配的元素
  1. 容量:
size();//返回容器中元素的个数
empty();//判断容器是否为空
resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除
resize(num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除
  1. 赋值:
assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身
assign(n, elem);//将n个elem拷贝赋值给本身
list& operator=(const list &lst);//重载等号操作符
swap(lst);//将lst与本身的元素互换
  1. 存取:
front();//返回第一个元素
back();//返回最后一个元素
  1. 反转排序:
reverse();//反转链表,比如lst包含1,3,5元素,运行此方法后,lst就包含5,3,1元素
sort(); //list排序

set/multiset

set中所有元素都会根据元素的键值自动被排序。set的元素不像map那样可以同时拥有实值和键值,set的元素即是键值又是实值。set不允许两个元素有相同的键值。不可以通过set的迭代器改变set元素的值,因为set元素值就是其键值,关系到set元素的排序规则。如果任意改变set元素值,会严重破坏set组织。换句话说,set的iterator是一种const_iterator。
set拥有和list某些相同的性质,当对容器中的元素进行插入操作或者删除操作的时候,操作之前所有的迭代器,在操作完成之后依然有效,被删除的那个元素的迭代器必然是一个例外
multiset特性及用法和set完全相同,唯一的差别在于它允许键值重复。

set和multiset的底层实现是红黑树

使用:

  1. 构造函数:
set<T> st;//set默认构造函数
mulitset<T> mst; //multiset默认构造函数
set(const set &st);//拷贝构造函数

//可以自定义键的排序规则
struct MyCompare {
	bool operator()(int v1,int v2) { return v1 > v2; }
};
set<int, MyCompare> s;
  1. 赋值:
set& operator=(const set &st);//重载等号操作符
swap(st);//交换两个集合容器
  1. 容量:
size();//返回容器中元素的数目
empty();//判断容器是否为空
  1. 插入删除:
insert(elem);//在容器中插入元素
erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg, end);//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器
erase(elem);//删除容器中值为elem的元素
clear();//清除所有元素
  1. 查找:
find(key);//查找键key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end()
count(key);//查找键key的元素个数
lower_bound(keyElem);//返回第一个key>=keyElem元素的迭代器
upper_bound(keyElem);//返回第一个key>keyElem元素的迭代器
equal_range(keyElem);//返回容器中key与keyElem相等的上下限的两个迭代器

pair

pair将一对值组合成一个值,这一对值可以具有不同的数据类型,两个值可以分别用pair的两个公有属性first和second访问

使用:

pair<string, int> pair1(string("name"), 20);
cout << pair1.first << " " << pair1.second << endl;

pair<string, int> pair2 = make_pair("name", 30);
cout << pair2.first << " " << pair2.second << endl;

pair<string, int> pair3 = pair2;
cout << pair3.first << " " << pair3.second << endl;

map/multimap

map中所有元素都会根据元素的键值自动排序。map所有的元素都是pair,同时拥有实值和键值,pair的第一元素被视为键值,第二元素被视为实值,map不允许两个元素有相同的键值
不可以通过map的迭代器改变map的键值, 因为map的键值关系到map元素的排列规则,任意改变map键值将会严重破坏map组织。如果想要修改元素的实值,那么是可以的
map和list拥有相同的某些性质,当对它的容器元素进行新增操作或者删除操作时,操作之前的所有迭代器,在操作完成之后依然有效,当然被删除的那个元素的迭代器必然是个例外
multimap和map的操作类似,唯一区别multimap键值可重复

map和multimap都是以红黑树为底层实现机制

使用:

  1. 构造函数:
map<T1, T2> mapTT;//map默认构造函数
map(const map &mp);//拷贝构造函数
  1. 赋值:
map& operator=(const map &mp);//重载等号操作符
swap(mp);//交换两个集合容器
  1. 容量:
size();//返回容器中元素的数目
empty();//判断容器是否为空
  1. 插入删除:
mapStu.insert(pair<int, string>(3, "小张")); //往容器插入元素,返回pair<iterator,bool>
mapStu.inset(make_pair(-1, "校长"));
mapStu.insert(map<int, string>::value_type(1, "小李"));

mapStu[3] = "小刘";
mapStu[5] = "小王";

erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器
erase(beg, end);//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器
erase(keyElem);//删除容器中key为keyElem的对组
clear();//删除所有元素
  1. 查找:
find(key);//查找键key是否存在,若存在,返回该键的元素的迭代器;/若不存在,返回map.end()
count(keyElem);//返回容器中key为keyElem的对组个数。对map来说,要么是0,要么是1。对multimap来说,值可能大于1
lower_bound(keyElem);//返回第一个key>=keyElem元素的迭代器
upper_bound(keyElem);//返回第一个key>keyElem元素的迭代器
equal_range(keyElem);//返回容器中key与keyElem相等的上下限的两个迭代器

STL算法

STL算法主要由头文件<algorithm>、<numeric>、<functional>组成。要使用STL中的算法函数必须包含头文件<algorithm>,对于数值算法须包含<numeric>,<functional>中则定义了一些模板类,用来声明函数对象

STL中算法大致分为四类:

  1. 非可变序列算法:指不直接修改容器内容的算法
  2. 可变序列算法:指可以修改容器内容的算法
  3. 排序算法:包括对序列进行排序和合并的算法、搜索算法以及有序序列上的集合操作
  4. 数值算法:对容器内容进行数值计算

查找算法

  1. adjacent_find:在iterator对标识元素范围内,查找一对相邻重复元素,找到则返回指向这对元素的第一个元素的ForwardIterator。否则返回last。重载版本使用输入的二元操作符代替相等的判断
  2. binary_search: 在有序序列中查找value,找到返回true。重载的版本使用指定的比较函数对象或函数指针来判断相等
  3. count:利用等于操作符,把标志范围内的元素与输入值比较,返回相等元素个数
  4. count_if: 利用输入的操作符,对标志范围内的元素进行操作,返回结果为true的个数
  5. equal_range: 功能类似equal,返回一对iterator,第一个表示lower_bound,第二个表示upper_bound
  6. find:利用底层元素的等于操作符,对指定范围内的元素与输入值进行比较。当匹配时,结束搜索,返回该元素的一个InputIterator
  7. find_end:在指定范围内查找“由输入的另外一对iterator标志的第二个序列”的最后一次出现。找到则返回最后一对的第一个ForwardIterator,否则返回输入的"另外一对"的第一个ForwardIterator。重载版本使用用户输入的操作符代替等于操作
  8. find_first_of: 在指定范围内查找“由输入的另外一对iterator标志的第二个序列”中任意一个元素的第一次出现。重载版本中使用了用户自定义操作符
  9. find_if:使用输入的函数代替等于操作符执行find
  10. lower_bound:返回一个ForwardIterator,指向在有序序列范围内的可以插入指定值而不破坏容器顺序的第一个位置。重载函数使用自定义比较操作
  11. upper_bound:返回一个ForwardIterator,指向在有序序列范围内插入value而不破坏容器顺序的最后一个位置,该位置标志一个大于value的值。重载函数使用自定义比较操作
  12. search:给出两个范围,返回一个ForwardIterator,查找成功指向第一个范围内第一次出现子序列(第二个范围)的位置,查找失败指向last1。重载版本使用自定义的比较操作
  13. search_n:在指定范围内查找val出现n次的子序列。重载版本使用自定义的比较操作

排序和通用算法

  1. inplace_merge:合并两个有序序列,结果序列覆盖两端范围。重载版本使用输入的操作进行排序
  2. merge:合并两个有序序列,存放到另一个序列。重载版本使用自定义的比较
  3. nth_element:将范围内的序列重新排序,使所有小于第n个元素的元素都出现在它前面,而大于它的都出现在后面。重载版本使用自定义的比较操作
  4. partial_sort:对序列做部分排序,被排序元素个数正好可以被放到范围内。重载版本使用自定义的比较操作
  5. partial_sort_copy:与partial_sort类似,不过将经过排序的序列复制到另一个容器
  6. partition:对指定范围内元素重新排序,使用输入的函数,把结果为true的元素放在结果为false的元素之前
  7. random_shuffle:对指定范围内的元素随机调整次序。重载版本输入一个随机数产生操作
  8. reverse:将指定范围内元素重新反序排序
  9. reverse_copy:与reverse类似,不过将结果写入另一个容器
  10. rotate:将指定范围内元素移到容器末尾,由middle指向的元素成为容器第一个元素
  11. rotate_copy:与rotate类似,不过将结果写入另一个容器
  12. sort:以升序重新排列指定范围元素。重载版本使用自定义的比较操作
  13. stable_sort:与sort类似,不过保留相等元素之间的顺序关系
  14. stable_partition:与partition类似,不过不保证保留容器中的相对顺序

删除和替换算法

  1. copy:复制序列
  2. copy_backward:与copy相同,不过元素是以相反顺序被拷贝
  3. iter_swap:交换两个ForwardIterator的值
  4. remove:删除指定范围内所有等于指定元素的元素。注意,该函数不是真正删除函数。内置函数不适合使用remove和remove_if函数
  5. remove_copy:将所有不匹配元素复制到一个制定容器,返回OutputIterator指向被拷贝的末元素的下一个位置
  6. remove_if:删除指定范围内输入操作结果为true的所有元素
  7. remove_copy_if:将所有不匹配元素拷贝到一个指定容器
  8. replace:将指定范围内所有等于vold的元素都用vnew代替
  9. replace_copy:与replace类似,不过将结果写入另一个容器
  10. replace_if:将指定范围内所有操作结果为true的元素用新值代替
  11. replace_copy_if:与replace_if,不过将结果写入另一个容器
  12. swap:交换存储在两个对象中的值
  13. swap_range:将指定范围内的元素与另一个序列元素值进行交换
  14. unique:清除序列中重复元素,和remove类似,它也不能真正删除元素。重载版本使用自定义比较操作
  15. unique_copy:与unique类似,不过把结果输出到另一个容器

排列组合算法

  1. next_permutation:取出当前范围内的排列,并重新排序为下一个排列。重载版本使用自定义的比较操作
  2. prev_permutation:取出指定范围内的序列并将它重新排序为上一个序列。如果不存在上一个序列则返回false。重载版本使用自定义的比较操作

算术算法

  1. accumulate:iterator对标识的序列段元素之和,加到一个由val指定的初始值上。重载版本不再做加法,而是传进来的二元操作符被应用到元素上
  2. partial_sum:创建一个新序列,其中每个元素值代表指定范围内该位置前所有元素之和。重载版本使用自定义操作代替加法
  3. inner_product:对两个序列做内积(对应元素相乘,再求和)并将内积加到一个输入的初始值上。重载版本使用用户定义的操作
  4. adjacent_difference:创建一个新序列,新序列中每个新值代表当前元素与上一个元素的差。重载版本用指定二元操作计算相邻元素的差

生成和异变算法

  1. fill:将输入值赋给标志范围内的所有元素
  2. fill_n:将输入值赋给first到first+n范围内的所有元素
  3. for_each:用指定函数依次对指定范围内所有元素进行迭代访问,返回所指定的函数类型。该函数不得修改序列中的元素
  4. generate:连续调用输入的函数来填充指定的范围
  5. generate_n:与generate函数类似,填充从指定iterator开始的n个元素
  6. transform:将输入的操作作用与指定范围内的每个元素,并产生一个新的序列。重载版本将操作作用在一对元素上,另外一个元素来自输入的另外一个序列。结果输出到指定容器

关系算法

  1. equal:如果两个序列在标志范围内元素都相等,返回true。重载版本使用输入的操作符代替默认的等于操作符
  2. includes:判断第一个指定范围内的所有元素是否都被第二个范围包含,使用底层元素的<操作符,成功返回true。重载版本使用用户输入的函数
  3. lexicographical_compare:比较两个序列。重载版本使用用户自定义比较操作
  4. max:返回两个元素中较大一个。重载版本使用自定义比较操作
  5. max_element:返回一个ForwardIterator,指出序列中最大的元素。重载版本使用自定义比较操作
  6. min:返回两个元素中较小一个。重载版本使用自定义比较操作
  7. min_element:返回一个ForwardIterator,指出序列中最小的元素。重载版本使用自定义比较操作
  8. mismatch:并行比较两个序列,指出第一个不匹配的位置,返回一对iterator,标志第一个不匹配元素位置。如果都匹配,返回每个容器的last。重载版本使用自定义的比较操作

集合算法

  1. set_union:构造一个有序序列,包含两个序列中所有的不重复元素。重载版本使用自定义的比较操作
  2. set_intersection:构造一个有序序列,其中元素在两个序列中都存在。重载版本使用自定义的比较操作
  3. set_difference:构造一个有序序列,该序列仅保留第一个序列中存在的而第二个中不存在的元素。重载版本使用自定义的比较操作
  4. set_symmetric_difference:构造一个有序序列,该序列取两个序列的对称差集(并集-交集)

堆算法

  1. make_heap:把指定范围内的元素生成一个堆。重载版本使用自定义比较操作
  2. pop_heap:并不真正把最大元素从堆中弹出,而是重新排序堆。它把first和last-1交换,然后重新生成一个堆。可使用容器的back来访问被“弹出”的元素或者使用pop_back进行真正的删除。重载版本使用自定义的比较操作
  3. push_heap:假设first到last-1是一个有效堆,要被加入到堆的元素存放在位置last-1,重新生成堆。在指向该函数前,必须先把元素插入容器后。重载版本使用指定的比较操作
  4. sort_heap:对指定范围内的序列重新排序,它假设该序列是个有序堆。重载版本使用自定义比较操作

适配器

C++提供了多种包装器(又称适配器)。这些对象用于给其他编程接口提供更一致或更合适的接口。其中包含容器适配器、函数对象适配器、迭代器适配器三种类型的适配器

容器适配器

容器适配器(Container Adapter)能对底层容器进行自定义接口,进而得到不同功能的新容器。queue、priority_queue和stack就是属于这个范畴,它们的底层实现都是deque。deque是双向的,那么当关闭一端的插入和一端的删除,这个东西也就变成了queue、priority_queue或者stack

迭代器适配器

迭代器适配器(Iterator Adapter)能够自定义迭代器遍历容器的同时对容器元素进行的操作,进而得到不同功能的迭代器。STL提供了很多应用于迭代器的适配器,主要有:有back_insert_iterator、front_insert_iterator、inser_iterator、reverse_iterator、istream_iterator、ostream_iterator、istreambuf_iterator、ostreambuf_iterator等等。下面介绍集中主要的迭代器适配器:

  • iostream iterators能将自己绑定一个iostream对象身上,从而获得输入输出的功能。如下面程序。对istream_iterator,用“end”来表示输入的结束,是因为这个istream_iterator是int型的,它的开始位置是cin,结束位置不定义就是EOF,当遇到了字母,也就是这个迭代器到达了EOF,所以就结束了,同时inserter函数调用v的insert函数,所以就实现了这么一次输入。而ostream_iterator将v中的元素拷贝到输出cout中,每个元素之间以空格分隔。结合流类迭代器和插入迭代器,可快速地实现输入输出的功能
deque<int> id;

copy(istream_iterator<int>(cin), istream_iterator<int>(), inserter(id, id.end()));//输入0 1 2 3 4 5 end

ostream_iterator<int> outline(cout, " ");
copy(id.begin(), id.end(), outline);//0 1 2 3 4 5
  • reserve iterators能将一般迭代器的行进方向进行逆转,可以很好地应用于从容器尾端开始的算法。如
copy(id.rbegin(), id.rend(), outline);//5 4 3 2 1 0
  • insert iterators可以将迭代器的赋值操作转变为插入操作。根据功能的不同,还分为用于尾端插入的back_insert_iterator,用于头端插入的front_insert_iterator,用于任意位置插入的insert_iterator。如
copy(ia+0, ia+2, front_inserter(id));//注意是先遍历0,所以先在最前面插入0,再在最前面插入1
copy(id.begin(), id.end(), outline);//1 0 0 1 2 3 4 5

copy(ia+3, ia+4, back_inserter(id));
copy(id.begin(),id.end(),outline);//1 0 0 1 2 3 4 5 3

deque<int>::iterator iter = find(id.begin(), id.end(), 5);
copy(ia+0, ia+3, inserter(id, iter));
copy(id.begin(), id.end(),outline);//1 0 0 1 2 3 4 0 1 2 5 3

函数对象适配器

函数对象适配器(Function Object Adapter),相比于其他适配器更加地灵活,可以自由地组合适配。函数适配器包括以下几种

  1. 联结(bind)。通过bind将仿函数与参数进行绑定,可实现算法所需的条件判断功能,如判断小于12的元素时,可用bind2nd(less(), 12);或判断12小于某元素时,可用bind1st(less(), 12)。bind1st与bind2nd的区别在于bind1st中参数是在符号的左边,bind2nd中参数是在符号的右边。而在C++11中提供了bind函数,这是一个通用的函数适配器。通过placeholders::_n指定是第几个参数,如
class func{
public:
    int operator()(const int &a,const int &b) {return a+b;}
};
for_each(v.begin(), v.end(), bind(func(), placeholders::_1, 9));//_1表示遍历时容器内元素作为第一个参数,而9为第二个参数
  1. 否定(negate)。指取反的操作,如判断不小于12的元素,可用not1(bind2nd(less(), 12))。此外还有not2,区别在于not1是构造一个与谓词结果相反的一元函数对象,而not2是构造一个与谓词结果相反的二元函数对象

  2. 组合(compose)。当算法的判断条件需要进行一些复杂的数学运算时,即可采用这种适配操作。有compose1和compose2,若对f(x)=3*x,g(y)=y+2,则compose1(f(x), g(y))=(y+2)*3;若对g1(x)=3*x,g2(x)=2*x,h(x)=a+b,则compose2(h(x), g1(x), g2(x))=3*x+2*x。如对每个元素v进行(v+2)*3操作,就可用compose1(bind2nd(multiplies(), 3), bind2nd(plus(), 2))

  3. 一般函数适配器。一般函数可以当做仿函数供STL算法使用,但无配接能力,需要将其包装成仿函数,其原理就是在仿函数的运算符()内执行其所包装的函数即可

  4. 成员函数适配器。这里将成员函数包装成仿函数,从而可使用成员函数搭配各种泛型算法。当容器内存储的是对象的实体时,需使用mem_fun_ref进行适配;当容器内存储的是对象的指针时,需使用mem_fun进行适配。如

class A {
public:
    A(int x = 0): val(x) {}
    void print() { cout << val << " "; }
private:
    int val;
};
//vector<A> vec; ...

for(int i = 0; i < vec.size(); ++i) vec[i].print();//1

for(vector<int>::const_iterator it = vec.begin(); it != vec.end(); ++it) it->print();//2

for(auto x : vec) x.print();//3

for_each(vec.begin(), vec.end(), mem_fun_ref(&A::print));//4
//若对vector<A*>,则用mem_fun
  1. reference_wrapper。由于bind()不知道生成的函数执行的时候传递的参数是否还有效,所以它选择按值传参而不是按引用传参。所以下面程序得到的result仍为0,此时要用ref()将变量转换为引用类型。ref()返回一个reference_wrapper对象,事实上,ref()就是用reference wrapper来包裹对象的一个简化写法
void add(int a, int b, int& r) { r = a + b; }
int result = 0;
auto f = bind(add, _1, 20, result);//result=0
//auto f = bind(add, _1, 20, ref(result));//result=100
f(80);

auto r = ref(result);
//等价于
referencce_wrapper<dectype(result)> r(result);

但是ref()返回的并不是真正的对象的引用若对一个类使用ref()函数,对其返回对象调用成员函数会报错,此时需要先对返回对象调用get()函数才能得到该对象(实际上reference wrapper是用指针表现出引用的所有特性,所以返回的应该是指针指向的对象),此后再调用成员函数

对reference wrapper,它的一大用处就是,stl容器提供的是value语义而不是reference语义,所以容器不支持元素为引用,而用reference_wrapper可以实现。如

list<int> l(10);
iota(l.begin(), l.end(), -4);

vector<reference_wrapper<int>> v(l.begin(), l.end());
// can't use shuffle on a list (requires random access), but can use it on a vector
shuffle(v.begin(), v.end(), mt19937{random_device{}()});
  1. function函数。类模板function是一个通用目的的函数包裹对象。与普通函数不同的是,它是一个类对象,可以复制、销毁、存储。缺点之一是它是模板类,使用时必须实例化特定类型才可以,不同类型又不能作为多态使用
int g_Minus(int i, int j) { return i - j; }
function<int(int, int)> f = g_Minus;//包装普通函数

template <class T>
T g_Minus(T i, T j) { return i - j; }
function<int(int, int)> f = g_Minus<int>;//包装模板函数

auto g_Minus = [](int i, int j){ return i - j; };
function<int(int, int)> f = g_Minus;//包装lambda表达式

struct Minus {
    int operator()(int i, int j) { return i - j; }
};
function<int(int, int)> f = Minus();//包装非模板类型函数对象

template <class T>
struct Minus {
    T operator() (T i, T j) { return i - j; }
};
function<int(int, int)> f = Minus<int>();//包装模板类型函数对象

class Math {
public:
    static int Minus(int i, int j) { return i - j; }
};
function<int(int, int)> f = &Math::Minus;//包装非模板类型类静态成员函数

class Math {
public:
    template <class T>
    static T Minus(T i, T j) { return i - j; }
};
function<int(int, int)> f = &Math::Minus<int>;//包装模板类型类静态成员函数

class Math {
public:
    int Minus(int i, int j) { return i - j; }
};
Math m;
function<int(int, int)> f = bind(&Math::Minus, &m, placeholders::_1, placeholders::_2);//包装非模板类型类对象成员函数

class Math {
public:
    template <class T>
    T Minus(T i, T j) { return i - j; }
};
Math m;
function<int(int, int)> f = bind(&Math::Minus<int>, &m, placeholders::_1, placeholders::_2);//包装模板类型类对象成员函数

C++11在模板和STL方面的修改

  1. 对于内置数组以及包含方法begin()和end()的类(如string)和STL容器,基于范围的for循环可简化为如下形式
double prices[5] = {4.99, 10.99, 6.87, 7.99, 8.49};
for(double x : prices) cout << x << endl;
//或使用更容易、更安全的方式
for(auto x : prices) cout << x << endl;
//也可以使用引用类型修改数组内的元素
for(auto & x : prices) x = 1.0;
  1. 新增了STL容器forward_list、unordered_map、unordered_multimap、unordered_set和unordered_multiset。其中forward_list是单向链表,只能沿一个方向遍历,与双向链表list容器相比在占用存储空间方面更经济。另外,模板array也是C++11中新增的
  2. 新增方法cbegin()和cend(),这些新方法将元素视为const。同理,crbegin()和crend()是rbegin()和rend()的const版本
  3. 为避免混淆,C++11中要求声明时两个尖括号要分开,而C++11中不再这样要求。如:
vector<list<int>> v;//OK in C++11
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值