第9章 顺序容器
- 指定容器大小的类型应该使用该容器的
size_type
类型 - 允许通过使用内置数组中的一对指针初始化容器
- 容器元素类型必须满足以下两个约束:
- 元素类型必须支持赋值运算(故不能是引用)
- 元素类型的对象必须可以复制(用于初始化和
push_back
等)
第11章 泛型算法
- 为了以降序排列
vector
,只需向sort
传递一对反向迭代器sort(vec.rbegin(), vec.rend());
第12章 类
- 声明一个类而不定义它(即前向声明)时,该类是一个不完全类型,只能以有限方式使用:
- 不知道包含哪些成员
- 只能用于定义指向该类型的指针及引用,或用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数
- 不能用于定义数据成员或该类的对象(故类不能具有自身类型的数据成员)
- 在一个源文件中一个类只能被定义一次,如果在多个文件中定义一个类,那么每个文件中的定义必须是完全相同的
- 在普通的非
const
成员函数中,this
的类型是一个指向类类型的const
指针 - 当我们希望类的数据成员(甚至在
const
成员函数内)可以被修改时,可将它们声明为mutable
。mutable
数据成员永远都不会且不能为const
。 - 如果函数在类定义体之外定义,则用于返回类型的名字在类作用域之外(否则在之内)。
- 在类定义体外定义的成员函数的返回类型如果使用由该类定义的类型,则必须使用完全限定名,这是因为在函数名被处理前,代码不在类作用域内。
- 类定义实际上是在两个阶段中处理:
- 首先编译成员声明
- 只有在所有成员出现之后,才编译它们的定义本身(因此成员函数可以使用类作用域中所有名字)
- 必须在类中先定义类型名字,才能将它们用作数据成员的类型,或者成员函数的返回类型或形参类型
- 一旦一个名字被用作类型名,该名字就不能被重复定义。(但若一个名字在类外作用域中首先被定义为类型名,类中可以先重新定义它再用作类型名)。
- 构造函数不能声明为
const
- 对非类类型的数据成员进行赋值或使用初始化式在结果和性能上都是等价的
-
成员被初始化的次序就是定义成员的次序,不管它们在初始化列表中顺序如何。因此以下程序非法:
class X { int i; int j; public: X(int val): j(val), i(j) {} };
- 当没有在初始化列表中指出或使用合成的默认构造函数时,对于内置和复合类型的成员(如指针和数组),只对定义在全局作用域中的对象才初始化。
- 对于一个没有定义默认构造函数(显式或隐式地)的类:
- 不能用作动态分配数组的元素类型
- 其静态分配数组必须为每个元素提供一个显式的初始化式
- 可以用单个实参来调用的非
explicit
构造函数定义了从形参类型到该类类型的一个隐式转换。 - 友元可以是普通的非成员函数,或前面定义的其他类的成员函数,或整个类。
- 友元不是授予友元关系的那个类的成员,故它们不受其声明出现部分的访问控制影响。
- 必须先定义包含成员函数的类,才能将成员函数设为友元。另一方面,不必预先声明类和非成员函数来将它们设为友元。
- 友元声明将已命名的类或非成员函数引入到外围作用域中。此外,友元函数可以在类的内部定义,该函数的作用域扩展到包围该类定义的作用域。用友元引入的类名和函数(定义或声明),可以像预先声明的一样使用。
const static
数据成员在类的定义体中初始化时,该数据成员仍必须在类的定义体之外进行定义,但此时不必再指定初始值。static
数据成员的类型可以是该成员所属的类类型,并可用作默认实参。- 在构造函数初始化列表中除了默认的方式(如
x()
)外,无法用其他方式(如传递一个数组)来初始化一个数组类型的成员变量。 typedef
在类中必须出现在使用其定义的类型别名的声明之前。
第13章 复制控制
- 复制构造函数、赋值构造函数和析构函数总称为复制控制(copy control),编译器自动实现这些操作,但类也可以定义自己的版本。
- 对于语句
string str = "hello";
,在创建str
时,编译器首先调用接受一个c风格字符串形参的string
构造函数,创建一个临时对象,然后编译器使用string
复制构造函数将str
初始化为那个临时对象的副本。 - 对于语句
C ci = string("hello");
,ci
的初始化是否正确取决于两点:- 类C的带一个
string
形参的ctor是否为explicit
(如果是explicit
则失败) - 类C的copy ctor对当前作用域是否可见
- 类C的带一个
-
若使用元素初始化列表初始化数组元素,则首先根据指定值创建数组元素类型的元素,然后用复制构造函数将该值复制到相应元素:
C ci[] = { string("hello"), // 单参数ctor + copy ctor C() // 默认ctor + copy ctor };
- 合成的复制构造函数将复制数组类型成员的每一个元素,合成的复制操作符也会给数组的每个元素赋值。
- 当copy ctor定义为
explicit
时,就不能用于向函数传递对象和从函数返回对象。 - 容器(不管是标准库容器还是内置数组)中的元素总是按逆序撤销
- 合成析构函数按对象创建时的逆序撤销每个非
static
成员(即按类中成员声明次序的逆序) - 因为不能指定任何形参,所以不能重载析构函数
- 即使我们编写了自己的析构函数,合成的析构函数仍然运行。
- 自定义的copy ctor必须是引用传递形参(而不是传值),否则会导致无限递归。