Effective C++阅读笔记
- 条款1 C++语言联邦
- 条款2 尽量不用#define
- 条款3 关于const
- 条款4 关于初始化
- 条款5 C++默默编写并调用了哪些函数
- 条款6 若不想编译器自动生成,则显式拒绝
- 条款7 为多态基类声明虚析构函数
- 条款8 别让异常逃离析构函数
- 条款9 不在构造和析构的过程中调用虚函数
- 条款10 令operator= 返回一个reference to *this
- 条款11 在operator= 中处理“自赋值”
- 条款12 复制对象时勿忘其每一个成分
- 条款13 以对象管理资源
- 条款14 在资源管理类中小心copying行为
- 条款15 在资源管理类中提供对原始资源的访问
- 条款16 成对使用new和delete时要采取相同的形式
- 条款17 以独立语句将newed对象置入智能指针
- 条款18 让接口容易被正确使用,不易被误用
- 条款19 设计class犹如设计type
- 条款20 宁以pass-by-reference-to-const替换pass-by-value
- 条款21 必须返回对象时,别返回其reference
- 条款22 将成员变量声明成private
- 条款23 宁可拿非成员函数和非友元函数替换成员函数
- 条款24 若所有参数都需要类型转换,采用非成员函数
- 条款25 写出不抛异常的swap函数
- 条款26 尽可能延后变量定义式的出现时间
- 条款27 尽量少做转型动作
- 条款28 避免返回handles指向对象内部成分
- 条款29 “异常安全”而努力是值得的
- 条款30 关于inlining函数
- 条款31 将文件间的编译依存关系降至最低
- 条款32 确定public继承塑模出is-a关系
- 条款33 避免遮掩继承而来的名称
- 条款34 区分接口继承和实现继承
- 条款35 考虑虚函数以外的其它选择(关于设计模式)
- 条款36 绝不重新定义继承而来的非虚函数
- 条款37 绝不重新定义继承而来的缺省参数值
- 条款38 通过复合塑模出has-a或“根据某物实现出”
- 条款39 明知而审慎地使用private继承
- 条款40 明知而审慎地使用多重继承
- 条款41 隐式接口和编译期多态
- 条款42 typename的双重意义
- 条款43 学习处理模板化基类内的名称
- 条款44 将与参数无关的代码抽离templates
- 条款45 运用成员函数模板接受所有兼容类型
- 条款46 需要类型转换时为模板定义非成员函数
- 条款47 使用traits classes表现类型信息
- 条款48 模板元编程TMP
- 条款49 了解new-handler的行为
- 条款50 了解new和delete的合理替换时机
- 条款51 编写new和delete时需固守常规
- 条款52 写了placement new 也要写placement delete
- 条款53 不要轻易忽视编译器的警告
- 条款54 关于TR1等标准程序库
- 条款55 关于Boost
条款1 C++语言联邦
C++的“次语言”,用哪一个模块遵守其特定的规则:
(1)C
(2)OO C++
(3)模板
(4)STL
条款2 尽量不用#define
宏有时不会进入符号表
对于单纯常量,最好以const对象或enums替换#define
对于形似函数的宏,最好用template inline函数代替#define
条款3 关于const
如果const出现在 * 左边,表示被指物是常量;如果出现在 * 右边,表示指针自身是常量;如果出现在 * 两边,则被指物和指针都是常量。
T* const,表示这个指针不能指向其它的东西,但是其所指的东西可以改动;
const T*表示所指的东西不可改动,但指针所指可以改动。
条款4 关于初始化
对于内置类型(比如int),对其进行手动初始化。
对于非内置类型,需要使用构造函数进行初始化。尽量使用构造函数初始化列表来初始化,而不是先=default再在构造函数函数体内执行赋值操作。
函数体内的static对象叫local static对象。为免除“跨编译单元初始化的顺序问题”,尽量以local static对象替换non-local static对象。
条款5 C++默默编写并调用了哪些函数
C++会为空类暗自创建default构造函数、拷贝构造函数、拷贝赋值操作符以及析构函数,所有这些都是public且inline的。
如果主动在类中声明构造函数,编译器不再创建default构造函数。
条款6 若不想编译器自动生成,则显式拒绝
如果不想编译器生成条款5中的函数,可以通过将相应的成员函数声明成private并且不予实现。
或者定义一个基类,将这些成员函数放在基类的private中,然后private继承它,这样可以避免直接声明成private时,类的成员函数和友元还能访问private。
条款7 为多态基类声明虚析构函数
带多态性质的基类应该定义一个虚析构函数,这是因为如果一个派生类对象经由基类指针删除,而基类含有非虚析构函数,会出现未定义的结果。 一个类只要有虚函数就应该定义虚析构函数。
如果一个类的设计目的不是作为基类使用,或不带多态性质,就不应该为其定义虚析构函数,比如string、vector等标准库容器。
纯虚析构函数导致一个类变成抽象类,即不能被实例化。 ~virtual F() = 0;
条款8 别让异常逃离析构函数
避免在析构函数里raise异常,如果可能有也要用“双保险”的调用,try~catch。
可以把产生异常的代码放到一个普通函数中,由用户调用。
条款9 不在构造和析构的过程中调用虚函数
在构造和析构器近,这类调用不会下降至派生类,即派生类对象内的基类部分会先于派生类部分被构造妥当,在基类构造期间,“虚函数不是虚函数”。
条款10 令operator= 返回一个reference to *this
为了实现连续赋值,赋值操作符必须返回一个reference指向操作符的左侧实参,即赋值操作符return *this。
条款11 在operator= 中处理“自赋值”
确保当对象自我赋值时operator= 行为正确。可以使用“identity test”、copy-and-swap等方式。
条款12 复制对象时勿忘其每一个成分
如果为一个类添加一个成员变量,必须同时修改copying函数(拷贝构造、拷贝赋值)。
(派生类的)copying函数应确保复制对象内的所有成员变量及所有基类成分。
基类成分往往是private,应该让派生类的拷贝函数调用相应的基类函数。
条款13 以对象管理资源
把资源放进对象内,可以依赖C++“析构函数自动调用机制”确保资源被释放。
为防止资源泄露,使用RAII对象,它们在构造函数中获得资源,在析构函数中释放资源。
RAII:Resource Acquisition Is Initialization,“资源取得的时机便是初始化时机”,也就是以对象管理资源。
常用的两个RAII类分别是shared_ptr和auto_ptr(智能指针)。
auto_ptr复制动作会使它(被复制物)指向null,即“受auto_ptr管理的资源没有一个以上的auto_ptr同时指向它”。
shared_ptr(引用计数型智能指针)可以多对一,在无人指向的时候自动删除,但不能打破环状引用。
条款14 在资源管理类中小心copying行为
复制RAII对象必须一并复制它所管理的资源。
普通的RAII类拷贝行为是:禁止复制(将copying操作声明为private)、对底层资源用“引用计数法”(shared_ptr),但其它行为也能被实现。
条款15 在资源管理类中提供对原始资源的访问
每一个RAII类都应该提供一个“取得其所管理资源”的办法。
对原始资源的访问可以是显示转换(get)或隐式转换。一般显示转换比较安全,隐式转换对客户比较方便。
条款16 成对使用new和delete时要采取相同的形式
如果new中使用[],则需要使用delete [](数组的情况);
如果new中不含[],则只用delete。
条款17 以独立语句将newed对象置入智能指针
如果一个语句中包含多个操作,比如调用函数,执行new,将new的对象置入智能指针,那么C++完成这三件事的次序可能有多种,如果调用夹在两者之间,一旦调用raise依次,就会导致内存泄漏。
而以独立语句将new的对象放进智能指针,编译器对于“跨越语句的各项操作”没有重排顺序的自由,所以调用和new–>智能指针是分开的。