• 7 类
  • 3. 类的其他特性
  • 4. 类的作用域
  • 5. 构造函数再探
  • 6. 类的静态成员
  • II C++标准库
  • 5. 额外的string操作(快速浏览,当需要使用一个特定操作时回过头来仔细阅读)
  • 10. 泛型算法
  • 2. 初识泛型算法
  • 3. 定制操作
  • I C++基础

    1 开始

    2 变量与基本类型

    1. 基本内置类型

    arithmetric type 算术类型

    1. 整型:
      • 字符和bool
      • 其他整型:带符号 signed、无符号 unsigned
    2. 浮点型: 单精度值、双精度值、扩展精度值

    convert 类型转换:
    建议:避免无法预知和依赖实验环境的行为。因为编译器无须(有时是不能)检测错误也能正常运行。
    提示:切勿混用带符号和无符号性。

    literal 字面值常量

    2. variable 变量

    - 变量定义(definition)
    	- 对于C++:变量可以互换对象
    	    - object,对象,一块能存储数据并具有某种类型的内存空间。
    	    - value,值,只读的数据。
    	- type specifer 类型说明符:(类型)关键字
    	- 初始化(initialized,创建变量时赋予一个初始值)与赋值(擦出当前值,以新值替代)		
    
    - **external**,声明declaration)而非定义
    

    3. compound type 复合类型

    1. 引用(reference):别名,给一个以存在的对象起了另一个名字。
        - 本质:间接访问。
        - 把引用和初始值对象做绑定
        - 定义时必须被初始化
        - 初始值必须是一个对象
        - 引用与初始值必须同类型
        - 初始化后无法改变引用对象
        - 无法定义引用的引用
     
    2. pointer 指针
        - 本质:简介访问
        - 与引用的不同:
            1. 本身就是对象,允许对指针赋值和拷贝,
            2. 在生命周期内可以先后指向多个不同的对象。 
            3. 无须在定义时赋值。若未被初始化,则有一个不确定的值。
        - 特性:
            - 指针类型与指向对象类型必须严格匹配。(两种情况除外)
        - 符号: 
            - \* : 解引用(*)(指针必须已经初始化)。
            - &
        - 指针的4种状态
            1. 指向一个对象;
            2. 指向紧邻对象所占空间的下一个位置。常见于循环/迭代中;
            3. 空指针,*nullptr*,指针没有指向任何对象,未被初始化。
                - e.g.  ``int *p = nullptr``;
                - `NULL`是预处理变量(preprocessor variable),值为0
            4. 无效指针:上述情况之外的其他所有值。e.g.指向数组中不存在的元素。
        - 指针和赋值:赋值永远改变的是等号左侧的对象。
        - 其他指针操作:条件、比较
        - void*指针:**可以存放任意对象的地址。无法确定地址的对象类型**。
    

    4. const限定符

    0.

        - const对象必须初始化
    	- 使用**external**关键字在多文件共享const对象
    

    1. 对常量的引用(reference to const)

        - 常量对象只能使用常量引用来引用
        - 初始化常量引用时可以使用临时量(temporary)对象
            - 临时量:当编译器需要一个空间来暂存表达式的求值结果时临时创建的一个未命名对象。
        - 常量引用可能引用一个非const对象:此时不允许常量引用修改它的值,但允许通过其他途径修改。
    

    2. 常量指针(pointer to const)

        - 常量对象只能使用常量指针来指向
        - const指针
        - **待续**
    

    3. 顶层const(top-level const)

        - 顶层const:指针本身是个常量。
        - 底层const:指针指向的对象是常量,可以改变指针的值。
        - 靠右的const是顶层const,靠左的是底层const。
            - ``int const *p1``
            - ``const int *p2``
        - 拷贝:**待续**
    

    4. 常量表达式(const expression)

    常量表达式是指值不会改变并且在编译过程就能得到计算结果的表达式。
    e.g. 字面值、用常量表达式初始化的const对象。

    constexpr变量

    允许将变量声明为constexpr类型由编译器验证变量是否为常量表达式。
    声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化。

    字面值类型:声明constexpr时用到的类型

    e.g. 算术类型、引用和指针
    函数体内定义的变量一般来说并非存放在固定地址中,因此constexpr指针不能指向这样的变量;相反的,定义于所有函数体之外的对象其地址固定不变,能用来初始化constexpr指针。
    特别的,允许函数定义一类有效范围超出函数体本身的变量,这类变量一样也有固定地址,因此能被constexpr指向和引用。
    指针和constexpr:如果constexpr声明中定义了一个指针,限定符constexpr仅对指针有效,与指针所指的对象无关。

    5. 处理类型

    1. 类型别名(type alias)

    两种方法:typedef、using

    2. auto:编译器通过初始值推算变量类型

    3. decltype:选择并返回操作数(结果)的数据类型

    注意:对于decltype的表达式来说,如果变量名加上了以对括号,编译器就会把它当成是一个表达式。变量是一种可以作为赋值语句左值的特殊表达式,所以这样的decltype就会得到引用类型。切记:decltype((variable))的结果永远是引用。

    6. 自定义数据类型

    预处理器(preprocessor):确保头文件多次包含仍能安全工作的常用技术。
    头文件保护符(header guard):依赖于预处理变量(#define#ifdef#ifndef#endif),作用是防止重复包含。

    3 字符串、向量和数组

    1. 命名空间的using声明

    2. 标注库类型string

    1. 定义和初始化string对象:

    直接初始化(string s("hiya");)和拷贝初始化(string s="hiya";)的区别是是否使用等号(=)。

    2. string对象上的操作:

    读写、size函数与string::size_type类型、比较、赋值、相加

    3. 处理string对象值中的字符

    3. 标注库类型vector

    1. 定义和初始化vector对象:

    值初始化(value-initialized,使用小括号);
    列表初始化(list-initialized,使用中括号)。

    2. 向vector对象中添加元素

    3. 其他vector操作:

    注意,ector对象的下标运算符可用于访问已存在的元素,而不能用于添加元素。

    4. 迭代器(iterator)

    1. 使用迭代器

    2. 迭代器运算(iterator arithmetic)

    迭代器的距离:类型是difference_type的到符号整数

    5. 数组

    1. 定义和初始化内置数组:

    (与vector不同)大小固定不变,不允许数组间的拷贝和赋值。

    2. 访问数组元素

    3. 指针和数组:标准库函数begin和end

    4. C风格字符串

    5. 与旧代码的接口:使用数组初始化vector对象

    6. 多维数组

    4 表达式

    1. 基础

    1. 基本概念

    组合运算符和运算对象

    运算符优先级

    运算对象转换

    里氏转换

    重载运算符(overloaded operator)
    左值和右值

    待续

    2. 优先级和结合律

    3. 求值顺序

    2. 算术运算符

    3. 逻辑和关系运算符

    4. 赋值运算符

    5. 递增和递减运算符

    6. 成员访问运算符:”.“、“->”

    7. 条件运算符/三目运算符

    8. 位运算符

    9. sizeof运算符

    10. 逗号运算符

    11. 类型转换

    1. 算术转换(arithmetic conversion)

    2. 其他隐式转换

    3. 显示转换

    强制类型转换(cast)

    static_cast

    任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast.
    使用:

    1. 把一个较大的算术类型赋给较小的类型
    2. 编译器无法自动执行的类型转换
      dynamic_cast

      支持运行时类型识别。

      const_cast

      “去掉const性质(cast away the const)”,将常量对象转换成非常量对象。 特点:

    3. 只能改变运算对象的底层const;
    4. 只能改变表达式的常量属性,不能改变表达式的类型。
      reinterpret_cast

      通常为运算对象的位模式提供较低层次上的重新解释。 虽然经过了重新解释,但本质上为发生变化,使用就可能运行时发生错误。 非常危险:其中的关键问题是类型改变了,但比哦安一起没有给出任何警告或者错误的提示信息。 WARNNING:本质上依赖于及其。要想安全的使用,就必须对涉及的类型和编译器实现转换的过程都非常了解。

      建议:避免强制类型转换
      旧式的强制类型转换

      包含:函数形式的强制类型转换、C语言风格的强制类型转换。

    12. 运算符优先级表

    5 语句

    1. 简单语句

    表达式语句(expression statement):执行表达式并丢弃掉求值结果。
    空语句(null statement) 复合语句(compound statement):也叫块(block)。

    2. 语句作用域

    3. 条件结构

    1. if语句

    2. switch语句

    4. 迭代语句

    1. while语句

    2. 传统的for语句

    3. 范围for语句

    4. do while语句

    5. 跳转语句

    1. break语句

    2. continue语句

    3. goto语句

    4. return语句

    6. try语句块和异常处理

    1. throw表达式

    2. try语句块

    鲁棒性

    3. 标准异常

    C++标准库的异常类的头文件:

    what()函数:异常类型的唯一成员函数,返回值是一个指向C风格字符串的const char *,目的是提供关于异常的一些文本信息。

    6 函数

    1. 函数基础

    函数(function):返回类型(return type)、函数名、形参(parameter)列表以及函数体(function body)。
    调用运算符(call operator):实参(argument)

    1. 局部对象

    生命周期(lifetime)
    局部变量(local variable),隐藏同名
    局部静态对象(local static object)

    2. 函数声明/函数原型(function prototype)

    建议在头文件中进行函数声明

    3. 分离式编译(separate compilation)

    2. 参数传递

    引用传递(passed by reference)或者传引用调用(called by reference) 值传递(passed by alue)或者传值调用(called by value)

    1. 传值参数

    2. 传引用参数

    避免拷贝 返回额外信息

    3. const形参和实参

    指针或引用形参与const:待续
    尽量使用常量引用

    4. 数组形参

    5. main:处理命令选项

    可以通过命令行向程序传递两个(可选的)形参给main函数。

    int main(int argc, char *argv[]){ return 0; }
    

    第二个形参argv是一个指向C风格字符串数组的指针;第一个元素是数组中字符串的数量。
    main函数也可定义成:

    int main(int argc, char **argv){ return 0; }
    

    6. 含有可变形参的函数

    initializer_list形参

    标准库类型,用于存储某种特定类型的值的数组。
    情况:实参数量未知但是全部实参的类型都相同。

    void error_msg(intializer_list<string> i1)
    {
        for (auto beg = i1.begin(); beg != i1.end(); ++beg)
            cout<< *beg << " ";
        cout << endl;
    }
    
    省略符形参
    void foo(param_list,...);
    
    可变参数模板

    待续

    3. 返回类型和return语句

    1. 无返回值函数

    2. 有返回值函数

    5. 特殊用途语言特性

    1. 默认实参(default argument)

    默认实参只能在参数列表的尾部

    2. 内联函数(inline)和constexpr函数

    内联函数可避免函数调用的开销:将函数指定为内联函数,通常就是将它在每个调用点上“内联地”展开。
    很多编译器不支持内链递归函数。
    constexpr函数是指能用于常量表达式的函数。函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句。
    内联函数和constexpr函数通常被定义在头文件内。

    3. 调试帮助

    assert预处理宏(preprocessor marco)
    asset(expr);
    

    首先对expr求值,如果表达式为假(即0),assert输出信息并终止程序的执行。

    NDEBUG预处理变量

    定义NDEBUG则assert则不会执行运行时检查。

    6. 函数匹配

    确定候选函数(candidate function):1.同名;2.声明在调用点可见。
    可行函数(viable function):1.形参数量与实参相等;2.实参类型与对应形参类型相同,或者可以实参类型转换。 寻找最佳匹配 含有多个形参的函数匹配:二义性调用。

    7. 函数指针

    函数指针指向的是函数而非对象。

    使用函数指针
    重载函数的指针
    函数指针形参

    形参可以是指向函数的指针.

    返回指向函数的指针
    将auto和decltype用于函数指针类型

    7 类

    1. 定义抽象数据类型(abstract data type)

    2. 访问控制和封装(encapsulation)

    访问说明符(access specifiers):privatepublic

    使用class和struct关键字

    唯一区别:struct的默认访问权限是public,而class则是private。

    1. 友元(friend)

    类可以允许其他类或者函数访问它的非公有成员。
    友元声明只能出现在类定义的内部,但是具体位置不限。
    友元不是类的成员,也不受所在区域的访问控制级别的约束。

    3. 类的其他特性

    1. 成员

    可变数据成员(mutable data member):即使在const成员函数中也可以改变一个可变成员的值。

    2. 返回*this的成员函数

    建议:对于公共代码使用私有功能函数。

    3. 类类型

    前向声明(forward declaration):仅声明类而暂时不定义,此时该类是一个不完全类型(incomplete type)。

    4. 友元

    类之间的友元关系:友元类
    令成员函数作为友元

    必须明确指出该成员函数属于哪个类。

    函数重载和友元

    需要对函数重载的一组函数中的每一个分别声明友元。

    友元声明和作用域
    struct X{
        friend void f() {}
        void h();
    };
    void f();
    void X::h()
    {
        return f();
    }
    

    4. 类的作用域

    1. 名字查找与类的作用于

    名字查找(name lookup):寻找与所用名字最匹配的声明的过程。

    5. 构造函数再探

    1. 构造函数初始值列表

    构造函数的初始值有时必不可少。 成员初始化的顺序:与在类定义中的出现顺序一致。最好尽量避免使用某些成员初始化其他成员。

    2. 委托构造函数(delegating constructor)

    一个委托构造函数使用它所属类的其他构造函数执行它的初始化过程,或者说它把它自己的一些职责委托给了其他构造函数。

    3. 默认构造函数的作用

    4. 隐式的类类型转换

    类类型转换只允许一步。
    类类型转换不是总有效。
    抑制构造函数定义的隐式转换:在类内声明构造函数时使用explicit。只对一个实参的构造函数有效,多个实参的构造函数不能用于执行隐式转换,所以无需explicit。
    explicit构造函数只能用于直接初始化。
    为转换显式地使用构造函数。
    标准库中含有显式构造函数的类:

    5. 聚合类(aggregate class)

    聚合类使得用户可以直接访问其成员,并且具有特殊的初始化语法形式。
    当一个类满足如下条件时,我们说它是聚合的:

    6. 字面值常量类

    字面值常量类的要求:

    6. 类的静态成员

    声明:static关键字。
    使用:使用作用域运算符直接访问静态成员。
    定义:static关键字值出现在类内部的声明语句。
    初始化:通常情况下,类的静态成员不应该在类的内部初始化。
    静态成员能用于某些场景,而普通成员不能。

    II C++标准库

    8 IO库

    1. IO类

    IO库类型和头文件: | 头文件 | 类型 | 类型(支持宽字符) | 功能 | |—-|—-|—-|—-| | iostream | istream | wistream | 从流读取数据 | | | ostream | wostream | 向流写入数据 | | | iostream | wiostream | 读写流 | | fstream | ifstream | wifstream | 从文件读取数据 | | | ofstream | wofstream | 向文件写入数据 | | | iosftream | wiofstream | 读写文件 | | sstream | istringstream | wistringstream | 从string读取数据 | | | ostringstream | wostringstream | 向string写入数据 | | | stringstream | wstringstream | 读写string |

    IO类型间的关系:ifstream和istringstream继承自istream。
    标准库是我们能忽略这些不同类型的流之间的差异,这是通过继承机制(inheritance)实现的。 e.g. 我们可以用>>读取数据,而不用关是从一个控制台窗口,一个磁盘文件,还是一个string读取。

    1. IO对象无拷贝和赋值

    2. 条件状态(condition state)

    IO操作一个与生俱来的问题是可能发生错误。一些错误是可恢复的,而其他错误则发生在系统深处,已经超出了应用程序可以修正的范围。因此IO类定义了一些函数和标志,可以帮助我们访问和操纵流的条件状态。
    由于流可能处于错误状态,因此代码通常应该在使用一个流之前检查它是否处于良好状态。确定一个流对象的状态的最简单法哪个法是将它当作一个条件来使用。
    注:while(cin>>word) ...的终止条件:流错误或者读到结束标识符。

    查询流的状态:

    与及其无关的iostate类型

    3. 管理输出缓冲

    每个输出流都管理一个缓冲区,用来保存程序读写的数据。
    有了缓冲机制,操作系统就可以将程序的多个输出操作组合成单一的系统级写操作。
    由于设备的写操作可能很耗时,允许操作系统将多个输出操作组合为单一的设备写操作可以带来很大的性能提升。
    缓冲刷新:数据真正写道输出设备或文件。缓冲区满,则需要刷新缓冲才能继续写入。

    刷新输出缓冲区:

    警告:如果程序崩溃,输出缓冲不会被刷新。

    关联输入和输出流:

    一个istream对象可以关联到另一个ostream,一个ostream也可以关联到另一个ostream。
    关联时会导致缓冲刷新。
    待续

    2. 文件输入输出

    ifstream,ofstream,fstream

    1. 使用文件流对象

    使用fstream代替iostream&
    成员函数open和close
    自动构造和析构

    2. 文件模式(file mode)

    每个流都有一个关联的文件模式:

    以out模式打开文件会丢弃已有数据

    保留被ofstream打开的文件中已有数据的唯一方法时显式制定app或in模式。

    每次调用open时都会确定文件模式

    3. string流

    1. 使用istringstream

    待续

    2. 使用ostringstream

    待续

    9. 顺序容器

    1. 顺序容器概述

    顺序容器提供了快速顺序访问元素的能力。 |名称|容器类型|访问|性能| |—-|—-|—-|—-| |vector|可变大小数组|快速随机访问|尾部之外插入删除可能很慢| |deque|双段队列|快速随机访问|头尾插入删除速度很快| |list|双向链表|双向顺序访问|在任何位置插入删除代价相同,代价比其他容器大| |forward_list|单向链表|单向顺序访问|在任何位置插入删除代价相同,代价比其他容器大| |array|固定大小数组,非内置数组,更安全、更容易使用|快速随机访问|不能增加删除元素| |string|与vector相似,专用于保存字符|随机访问|尾部插入删除速度快|

    2. 容器库概览

    对容器可以保存的元素类型的限制

    1. 迭代器

    迭代器范围(iterator range)由一对迭代器表示,两个迭代器分别指向同一个容器中的元素或者尾元素之后的位置(one past the last element)。这两个迭代器通常被称为begin和end,它们标记了容器中元素的一个范围。这种元素范围被称为左闭合区间(left-inclusive interval),其标准数学描述为[begin,end)

    2. 容器类型成员

    反向迭代器:反向遍历容器的迭代器,执行++操作,会得到上一个元素。
    类型别名:必须显式使用类名与作用域运算符。元素类型value_type,引用reference或const_reference

    3. begin和end成员

    带r的版本返回反向迭代器;
    带c的版本返回const迭代器,只有对const对象才能得到const版本。

    4. 容器定义和初始化

    将一个容器初始化为另一个容器的拷贝
    列表初始化
    与顺序容器大小相关的构造容器

    标准库array具有固定大小

    声明时必须同时指定元素类型和大小。
    默认构造的array是非空的:包含了与其大小一样多的元素。
    我们不能对内置数组进行拷贝和对象赋值,但array无此限制。

    5. 赋值和swap

    由于右边运算对象的大小可能与左边运算对象的大小不同,因此array类型不支持assign,也不允许用花括号包围的值列表进行赋值。

    使用assign(仅顺序容器)

    相对于赋值‘=’,assign允许一个不同但相容的类型赋值。

    使用swap

    swap操作交换两个相同容器的内存(除array外,只是交换了容器的内部数据结构)。
    除array(会真正交换元素)外,swap不进行任何拷贝、删除或插入,因此可以保证常数时间内完成。
    除string外,指向容器的迭代器、引用和指针在swap操作之后都不会失效。
    建议:使用非成员版本的swap

    6. 容器大小操作

    size(forward_list不支持)、empty、max_size

    7. 关系运算符

    比较两个容器实际上是进行元素的逐对比较。工作方式与string关系运算类似:

    3. 顺序容器操作

    1. 向顺序容器添加元素

    使用push_back

    array和forward_list不支持。
    关键概念:容器元素是拷贝。

    使用push_front

    vector和string不支持。

    insert:在容器中的特定位置添加元素

    vector、deque、list、string都支持,forward_list提供了特殊版本的insert成员。

    insert:插入范围元素
    使用insert的返回值

    一个等价于push_front的insert循环:

    list<string> lst;
    auto iter=lst.begin();
    while(cin>>word)
        iter=lst.insert(iter,word);//等价于调用push_front
    
    使用emplace操作

    这些操作直接构造元素而不是拷贝元素。
    emplace_front、emplace和emplace_back分别对应push_front、insert和push_back

    2. 访问元素

    如果容器中没有元素,访问操作的结果是未定义的。
    front成员函数:返回首元素的引用。
    back成员函数:返回尾元素的引用,不支持forward_list。

    访问成员函数返回的是引用
    下标操作和安全的随机访问

    提供快速随机访问的容器(string、ector、deque和array)也都提供下标运算符。下标运算符不检查合法。
    at成员函数:安全的访问。

    3. 删除元素

    erase:forward_list有特殊版本 ,从容器内部删除一个或迭代器范围元素。 pop_back:forward_list不支持,删除尾元素。
    pop_front:vector和string不支持,删除首元素。
    clear:删除所有元素。

    4. 特殊的forward_list操作

    原理:改变给定元素之后的元素。
    insert_after、emplace_after、erase_after
    forward_list定义了before_begin,返回了一个首前(off-the-beginning)迭代器。这个迭代器允许我们在链表首元素之前并不存在的元素“之后”添加或删除元素(亦即在链表首元素之前添加删除元素)。

    5. 改变容器大小

    resize函数:不支持array

    6. 容器操作可能使迭代器失效

    建议:管理迭代器,每次改变容器的操作之后都必须正确地重新定位迭代器。

    编写改变容器的循环程序

    必须考虑迭代器、引用和指针可能失效的问题。

    不要保存end返回的迭代器

    在添加/删除元素后,原来end返回的迭代器总是会失效。因此,添加或删除元素的循环程序必须反复调用end,而不能在循环之前保存end返回的迭代器,一直当容器末尾使用。通常C++标准库的实现中end()操作都很快,部分就是因为这个原因。

    4. vector对象是如何增长的

    vector为了避免频繁内存分配和释放的代价,标准库实现通常会分配比空间需求更大的内存空间作为备用,以保存更多的新元素。

    管理容量的成员函数

    capacity():不重新分配内存空间的话可以容纳多少个元素。
    reserve():通知容器应该准备保存多少个元素/分配至少能容纳nge元素的内存空间。
    shrink_to_fit():请将capacity()减少为与size()相同大小,退回不需要的内存空间。但它只是一个请求,具体实现可以选择忽略此请求,即并不保证一定退回。

    capacity和size

    size():是指已经保存的元素数目。
    分配策略遵循原则:确保用push_back向vector添加元素的操作有高效率。从技术角度说,就是通过在一个初始为空的ector上调用n次的push_back来创建一个n个元素的ector,所花费的时间不能超过n的常数倍。

    5. 额外的string操作(快速浏览,当需要使用一个特定操作时回过头来仔细阅读)

    大部分额外操作的作用:

    1. 构造string的其他方法

    substr操作

    2. 改变string的其他方法

    append和replace函数
    改变string的多种重载函数

    3. string搜索操作

    string类提供了6个不同的搜索函数,每个函数都有4个重载版本。
    搜索操作返回指定字符出现的(string::size_type类型的)下标值,如果未找到则返回string::npos(static,const string::size_type;unsigned,初始化为值-1,意味着npos等于任何string最大的可能大小,用带符号类型int保存不是一个好主意):

    指定在哪里开始搜索
    逆向搜索

    4. compare函数

    5. 数值转换

    to_string()、 stoi(s,p,b)、 stol(s,p,b)、 stoul(s,p,b)、 stoll(s,p,b)、 stoull(s,p,b)、 stof(s,p)、 stod(s,p)、 stoi(s,p)

    6. 容器适配器(adaptor)

    本质上,一个适配器是一种机制,能使某种事物的行为看起来像另外一种事物一样。
    除了顺序容器外,标准库还定义了三个顺序容器适配器:stack、queue、priority_queue。

    定义一个适配器
    栈适配器
    队列适配器

    10. 泛型算法

    1. 概述

    大多数算法都定义在头文件algorithm中。标准库还在头文件numeric中定义了一组数值泛型算法。

    算法如何工作

    只要有一个迭代器可用来访问元素,就完全不依赖与容器类型(甚至无须理会保存元素的是不是容器)。

    迭代器令算法不依赖于容器
    但算法依赖于元素类型的操作

    元素类型支持该运算符,算法才可以操作。
    不过,大多数算法提供了方法,允许使用自定义的操作来代替默认的运算符。
    关键概念:算法永远不会执行容器的操作。

    2. 初识泛型算法

    输入范围:除少数例外,标准库算法都对一个范围内的元素进行操作。该类算法总是使用指向要处理的第一个元素和尾元素之后位置的迭代器作为参数。
    使用范围中元素的方式:读取元素、改变元素、重排元素顺序。

    1. 只读算法

    find()、 count()、 accumulate()、 equal() 算法和元素类型:对于该类算法,通常最好使用 cbegin() 和 cend()。
    操作两个序列的算法:用一个单一迭代器表示第二个序列的算法都假定第二个序列至少与第一个一样长。

    2. 写容器元素的算法

    注意:当使用这类算法时,必须注意确保序列原大小至少不小于我们要求算法写入的元素数目。原因:算法不会执行容器操作,因此算法自身不可能改变容器大小。

    算法不检查写操作。

    介绍back_inserter(插入迭代器):一种向容器中添加元素的迭代器。

    拷贝算法:向目的位置迭代器指向的输出序列中的元素写入数据的算法。copy()、 replace()、 replace_copy().

    3. 重排容器元素的算法

    重排容器中的元素顺序:sort()

    消除重复元素:unique()
    使用unique:返回一个指向不重复值范围末尾的迭代器。此位置之后的元素仍然存在,但不知道值是什么。

    使用容器操作删除元素:

    3. 定制操作

    1. 向算法传递函数

    谓词(predicate):一元谓词(unary predicate, 只接受单一参数)和二元谓词(binary predicate,意味着有两个参数)。
    排序算法:stable_sort算法,大小重排的同时,保持相同长度的单词按字典序排列。

    2. lambda表达式(lambda expression)

    介绍lambda:

    3. lambda捕获和返回