1. 译序
- C++是一个难学易用的语言。
- C++的难学,体现在广博的语法,背后的语义;语义背后的深层思维;深层思维背后的对象模型。
- C++四种不同而又相辅相成的编程泛型:基于过程型,基于对象型,面向对象型,一般模型。
- 衍生的复合术语:base class,derived class,super class,subclass,class templates等。
- object和type,中译词“对象”和“类型”。更贴切的是“物件”和“型别”。
2. 导读部分
- 本书的目的是探讨如何有效运用C++。书中提出的忠告有两类:一般性的设计策略和带有具体细节的特定语言特性。
- 设计上的讨论大多是选择题,不同做法完成同一件工作的情况下,选择:
- inheritance(继承)还是templates(模板)。
- public继承还是private继承。
- private继承还是composition(复合)。
- member函数还是non_member函数。
- pass_by_value还是pass_by_reference。
3. 条款一:让C++成为一个语言联邦
- C++发展与C with class,但拥有不同于C with class的观念,特性和编程战略,比如Exceptions,templates,STL等。
- C++多重范型编程语言:过程型(procedural)面向对象型(object-oriented)函数型(functional)泛型(generic)元编程型(metaprogramming)。
- C++能力强大,弹性十足,但所有适当的方法都有例外,合理运用这些强大的特性,方法是将C++视为由相关语言组成的联邦。
- C++语言主要的次语言:
- C:C++有时解决问题的方式是高级的C语言,但是C缺乏高级特性。
- Object-Oriented C++:这部分是C with class的诉求。classes(构造析构)encapsulation(封装)inheriance(继承)polgmorphism(多态)virtual函数(动态绑定)
- Template C++:C++泛型编程部分,template威力强大,能带来崭新的编程范式,TMP:模板元编程。
- STL:STL是template程序库,它对容器(containers)迭代器(iterators)算法(algorithms)以及函数对象(function-objects)的规约有很强的紧密配合与协调。
- 准则:
- C-like(内置类型)而言:pass_by_value 比 pass_by_reference高级。
- C part of C++移往Object-Oriented C++,由于构造和析构存在,pass_by_reference_to_const往往更好。
- Template C++尤其如此。
- STL中,迭代器和函数对象都在C指针上构造的,旧式的C pass_by_value再次适用。
4. 条款二:尽量以const eunm inline 替换 #define
- #define 不被视为语言的一部分。define的名称不在符号表中,由于define定义值引发的问题将很难定位。
- 常量替代宏 const double ASP = 1.653;
- 两种特殊的情况:①定义常量指针,有必要把指针也声明为cosnt。②class专属常量,需要声明为静态成员。作用域也限定在了class中。
- #define无法创建一个class专属常量,#define并不重视作用域。
- 旧式编译器可能不允许在其声明式上获得初值,替代方法是“the enum hack”。
class GamePlayer { private: eunm {NumTurns = 5}; }; // enum hack在某些行为上像define,无法获得地址。 // enum hack是模板元编程的基础技术。
- 取代宏函数的方案:template inline函数。
template <typename T> inline void callWithMax(const T& a, const T& b) { f ( a > b ? a : b ); }
- 单纯常量,以const对象或者enum替换#define。
- 形似函数的宏,改用inline函数替代#define。
5. 条款三:尽可能使用const
- const语法变化多端,但是并不高深莫测。const在星号左边,被指物是常量。const在星号右边,指针自身是常量。
- const出现在类型名的两侧时,效果是一样的。
- STL迭代器和const:STL迭代器是以指针为根据塑模出来的,迭代器等同于T*。
const std::vector<int>::iterator iter = vec.begin(); // iter 作用是T* const, iter++ 是不允许的,*iter = 10;是允许的。 std::vector<int>::const_iterator cIter = vec.begin(); // iter 作用是const T*,*cIter = 10; 是不允许的,++cIter;是允许的。
- const面向函数声明时的应用:const与返回值,参数,函数自身(成员函数)关联。
- 返回值是const:函数返回值不期望被修改时使用,多用于左值的运算符重载,例如 * 的重载。这种情况下参考基础类型在相同情况的的表现。
- const成员函数:
- const成员函数的目的是确认该函数可用于const对象上。
- 可以知道哪个函数可以修改对象的内容,而哪些不行。
- 使操作cosnt对象称为可能。
- 两个成员函数只是常量性不同,可以被重载,const是函数签名的一部分。
- 实际程序中,const对象大多用于passed by pointer_to_const 或者 passed by reference_to_const的传递结果。
- 成员函数时const,分为两种概念行为,bitwise constness 和 logical constness
- bitwise const 认为成员函数不修改任何成员变量时,才能说是const,符合C++对常量性的定义。const成员函数不能修改任何non-static成员变量。
- 很多函数不十足具备bitwise const特性,却能通过bitwise测试,例如函数返回指针成员所指向的内容,在函数外部可以修改成员值。
- 以上的情况导出了logical constness,const成员函数可以修改它所处理的对象内部某些bit,但是只有在客户端监测不到时才能如此。
- 对象有在const成员函数中修改no-static成员的需求,C++引入了mutable,释放了no-staic成员变量的bitwise constness 约束。
- 在const和non-const成员函数中避免重复
- 当成员函数需要const版本和非const版本时,使用const版本实现non-const版本。
- 步骤:实现const版本;返回类型为const引用;函数类型是const。实现non-const版本;使用static_cast把*this转为const;访问成员;对成员类型进行const_cast,去const属性。
- 反向的const调用non-const模板是错误的,违反了const的本质属性。
- const成员函数的目的是确认该函数可用于const对象上。
6. 条款四:确定对象被使用前已经被初始化
- 赋值和初始化的区别:构造函数内部都叫赋值,在初始化列表中的行为叫初始化。
- C++有固定的成员函数初始化次序。base class 早于 derived classes。成员的初始化列表按声明次序初始化。
- non-local static对象初始化次序:
- 函数内部的static对象时local static,其余的static对象都是non-local static。
- 两个non-local static 互相依赖(处于不同的编译单元),两个对象的初始化次序没有明确的定义。
- 解决这类情况的方式是把static对象放在static函数中定义返回。
FileSystem & tfs { static FileSystem fs; return fs; }