C++ Primer(第4版)-第5部分:高级主题

  1. 异常处理
    不存在数组或函数类型的异常。相反,如果抛出一个数组,被抛出的对象转换为指向数组首元素的指针,类似地,如果抛出一个函数,函数被转换为指向该函数的指针。

  2. 不要抛出指针

  3. 析构函数不要抛出异常

  4. 除下面几种可能的区别之外,异常的类型与 catch 说明符的类型必须完全匹配:

  5. 命名空间
    命名空间为防止名字冲突提供了更加可控的机制,命名空间能够划分全局命名空间,这样使用独立开发的库就更加容易了。
    一个命名空间是一个作用域,通过在命名空间内部定义库中的名字,库的作者(以及用户)可以避免全局名字固有的限制。

  6. 命名空间的定义:
    namespace cplusplus_primer
    {

    1. class Sales_item { /* ... */};<br />
    2. Sales_item operator+(const Sales_item&, const Sales_item&);<br />
    3. class Query { /* ... */}; <br />
    4. class Query_base { /* ... */};<br />

    }

  7. 从命名空间外部使用命名空间成员
    (1)总是使用限定名
    namespace_name::member_name
    (2)编写 using 声明来获得对我们知道将经常使用的名字的直接访问:
    using cplusplus_primer::Query;
    在这个 using 声明之后,程序可以无须 cplusplus_primer 限定符而直接使用名字 Query

  8. 命名空间可以是不连续的
    命名空间可以在几个部分中定义。命名空间由它的分离定义部分的总和构成,命名空间是累积的。一个命名空间的分离部分可以分散在多个文件中,在不同文本文件中的命名空间定义也是累积的。

  9. 接口和实现的分离
    可以用与管理自己的类和函数定义相同的方法来组织命名空间:
    (1) 定义类的命名空间成员,以及作为类接口的一部分的函数声明与对象声明,可以放在头文件中,使用命名空间成员的文件可以包含这些头文件。
    (2) 命名空间成员的定义可以放在单独的源文件中。

  10. 全局命名空间
    定义在全局作用域的名字(在任意类、函数或命名空间外部声明的名字)是定义在全局命名空间中的。
    因为全局命名空间是隐含的,它没有名字,所以记号 ::member_name 引用全局命名空间的成员。

  11. 嵌套命名空间
    一个嵌套命名空间即是一个嵌套作用域——其作用域嵌套在包含它的命名空间内部。
    嵌套命名空间中的名字遵循常规规则:外围命名空间中声明的名字被嵌套命名空间中同一名字的声明所屏蔽。嵌套命名空间内部定义的名字局部于该命名空间。外围命名空间之外的代码只能通过限定名引用嵌套命名空间中的名字。

  12. 嵌套命名空间中成员的名字由外围命名空间的名字和嵌套命名空间的名字构成。
    例如,嵌套命名空间 QueryLib 中声明的类的名字是 cplusplus_primer::QueryLib::Query

  13. 未命名的命名空间
    未命名的命名空间在定义时没有给定名字。
    未命名的命名空间与其他命名空间不同,未命名的命名空间的定义局部于特定文件,从不跨越多个文本文件。

  14. 命名空间成员的使用——使用using声明

一个 using 声明一次只引入一个命名空间成员,它使得无论程序中使用哪些名字,都能够非常明确。
using 声明中引入的名字遵循常规作用域规则。从 using 声明点开始,直到包含 using 声明的作用域的末尾,名字都是可见的。外部作用域中定义的同名实体被屏蔽。

  1. 命名空间别名
    可用命名空间别名将较短的同义词与命名空间名字相关联,例如:
    namespace primer = cplusplus_primer;
    一个命名空间可以有许多别名,所有别名以及原来的命名空间名字都可以互换使用。

  2. using 指示

using 指示以关键字 using 开头,后接关键字 namespace,再接命名空间名字。如果该名字不是已经定义的命名空间名字,就会出错。
using 指示使得特定命名空间所有名字可见,没有限制。短格式名字可从 using 指示点开始使用,直到出现 using 指示的作用域的末尾。

  1. 实参相关的查找与类类型形参
    std::string s;
    getline(std::cin, s);
    std::string s;
    它在命名空间 std 中查找并找到由 string 类型定义的 getline 函数。

  2. 重载与命名空间
    两个不同命名空间的成员的函数不能互相重载。
    同一个命名空间可以包含一组重载函数成员。

  3. 重载与 using 声明
    如果命名空间内部的函数是重载的,那么,该函数名字的 using 声明声明了所有具有该名字的函数。如果命名空间 NS 中有用于 int 和 double 的函数,则 NS::print 的 using 声明使得两个函数都在当前作用域中可见。

  4. 重载与 using 指示
    using 指示将命名空间成员提升到外围作用域。如果命名空间函数与命名空间所在的作用域中声明的函数同名,就将命名空间成员加到重载集合中:

  5. 多重继承
    派生类的构造函数可以在构造函数初始化式中给零个或多个基类传递值:

  6. 构造的次序
    构造函数初始化式只能控制用于初始化基类的值,不能控制基类的构造次序。
    基类构造函数按照基类在类派生列表中的出现次序调用。

  7. 转换与多个基类
    在单个基类情况下,派生类的指针或引用可以自动转换为基类的指针或引用,对于多重继承也是如此,派生类的指针或引用可以转换为其任意基类的指针或引用。

  8. 多个基类可能导致二义性
    可以通过指定使用哪个类解决二义性:ying_yang.Endangered::print(cout);
    避免潜在二义性最好的方法是,在解决二义性的派生类中定义函数的一个版本。

  9. 虚继承
    虚继承是一种机制,类通过虚继承指出它希望共享其虚基类的状态。在虚继承下,对给定虚基类,无论该类在派生层次中作为虚基类出现多少次,只继承一个共享的基类子对象。共享的基类子对象称为虚基类。

  10. 特殊的初始化
    从具有虚基类的类继承的类对初始化进行特殊处理。在虚派生中,由最低层派生类的构造函数初始化虚基类,而且是先构造虚基类,然后构造非虚基类。
    任何直接或间接继承虚基类的类一般也必须为该基类提供自己的初始化式。

  11. C++ 提供下面两种方法分配和释放未构造的原始内存
    (1) allocator 类,它提供可感知类型的内存分配。这个类支持一个抽象接口,以分配内存并随后使用该内存保存对象。
    (2) 标准库中的 operator new 和 operator delete,它们分配和释放需要大小的原始的、未类型化的内存。

  12. allocator 类