1 让自己习惯C++
条款01: 视C++为一个语言联邦
View C++ as a federation oflanguages
– C。说到底C++仍是以C为基础。区块(blocks)、语句(statements)、预处理器(preprocessor)、内置数据类型(built-in data types)、数组(arrays)、指针(pointers)等统统来自C。局限:non-templates non-exceptions non-overloading
– Object-Oriented C++。Classes(包括构造函数和析构函数),封装(encapsulation)、继承(inheritace)、多态(polymorphism)、virtual函数(晚捆绑)……等等。
– Template C++。泛型编程部分。
– STL。
※
C++ 高效编程守则视状况而变化,取决于你使用C++的哪一部分。
条款02: 尽量以const,enum,inline替换#define
Prefer consts,enums,and inlines to #define
– 以编译器替换预处理器。即尽量少用预处理
– #define 可能并不进入符号表(symbol table)。
Const:
• 常量指针(constant pointers)
– 例:不变的char* -based 字符串(最好使用string)
Const char* const authorName =“Scott Meyers”;
Const char* const authorName =“Scott Meyers”;
• Class专属常量
– 为了确保此常量至多只有一份实体,必须让它成为一个static成员:
ClassGamePlayer {
Private:
static const int NumTurns = 5 ; //常量声明
};
– 注意:只要不取它的地址,你可以声明并使用它们而无需提供定义。
– 如果你取某个class专属常量的地址或者编译器报错,你就必须提供定义。
如:const intGamePlayer::NumTurns;
由于class常量已在声明时获得初值(static),因此定义时不用再设初值。如果编译器还不允许这样,可以使用在声明外面定义赋予初值。如:const int GamePlayer::NumTurns = 5;如果还是报错,可以选择用 enum 关键字。
如:enum{ NumTurns =5};…
• Template Inline代替宏
#define CALL_WITH_MAX(a,b) f((a) > (b)) ? (a) :(b))
int a = 5,b = 0
CALL_WITH_MAX(++a,b); //a被累加两次
CALL_WITH_MAX(++a,b+10);//a被累加一次
template<typename T>
inline void callWithMax(cosnt T &a, cosnt T &b)
{
f(a > b ? a : b);
}
enum
1 比较像#define而不像const。取一个enum的地址不合法,所以可以防止pointer或reference指向你的某个整数常量。
2 模板元编程的基础技术
※
- 对于单纯常量,最好以const对象或enums替换#defines;
- 对于形似函数的宏,最好改用inline函数替换#defines。
条款03: 尽可能使用const
Use const whenever possible
– 如果关键字const出现在星号左边,表示被指类型是常量;如果出现在星号右边,表示指针自身是常量;如果在星号两边,表示被指类型和指针两者都是常量。
– Const最具有代表性的是函数声明时的应用。Const可以和函数返回值、各参数、函数自身产生关联。
– 令函数返回一个常量值,往往可以降低因客户错误而造成的意外,而不至于放弃安全性和高效性。
If(a * b = c)
– Const 成员函数
• 1)它们使class接口比较容易理解。
• 2)它们使“操作const对象”成为可能。
– 如果函数的返回类型是个内置类型,那么改掉函数返回值就不可能合法。
– Bitwise constness(physical constness)
• 成员函数只有在不更改对象的任何成员变量(static除外)时才可以说是const。也就是说它不更改对象内任何一个位(bit)。
– Logical constness
• 一个const成员函数可以修改它所处理的对象内某些bits,但只有在客户端侦测不出的情况下才可以。
– Mutable(可变的)关键字可以释放掉non-static成员变量的bitwise constness约束;
– 在const和non-const成员函数中避免重复
Const成员函数调用non-const成员函数是一种错误行为,因为对象有可能因此被改动。
– Const_cast
• 用法:const_cast<type_id>(expression)
– 该运算符用来修改类型的const或volatile属性。
– 常量指针被转化成非常量指针,并且仍然指向原来的对象;
– 常量引用被转换成非常量引用,并且仍然指向原来的对象;
– 常量对象被转换成非常量对象;
– Static_cast
• 用法:static_cast<type_id>(expression)
• 该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。
※
- 将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体;
- 编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”(conceptual constness);
- 当cosnt和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。
条款04: 确定对象被使用前已被初始化
Make sure that objects are initialized beforethey’re used
– 读取未初始化的值会导致不明确的行为。
l 对象的初始化何时一定发生,何时不一定发生。
– 对于无任何成员的内置类型,必须手工完成此事。
– 对于内置类型以外的任何其他东西,初始化责任落在构造函数(constructors)身上。确保每一个构造函数都将对象的每一个成员初始化。
– C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。
– 初始化列表免于先调用 default构造函数然后再调用赋值操作符,只需调用一次拷贝构造函数。更高效。
– 构造函数的最佳写法是,使用 member initialization list(成员初始化表)如:
ABEntry::ABEntry(char&name,char& address,list &phones)
:theName(name),theAddress(address),thePhones(phones)
{
…….
}
• 编译器会为用户自定义类型(user-definedtypes)之成员变量自动调用default构造函数--- 如果那些成员变量在“成员初始化列表”中没有被指定初值的话。
• 成员变量是 const 或references,它们就一定需要初值,不能被赋值。
• C++有着十分固定的“成员初始化次序”。Base classes 更早于其derived classes 被初始化,而class的成员变量总是以其声明次序被初始化。
• Static 对象,其有效时间从被构造出来直到程序结束为止,因此stack和heap-based对象被排除。
– C++对于“定义于不同的编译单元内的non-localstatic对象”的初始化相对次序并无明确定义。
• 小方法:将每一个non-localstatic对象放到自己的专属函数内(该对象在此函数内被声明为static),这些函数返回一个reference指向它所含的对象。
※
• 为内置对象进行手工初始化,因为C++不保证初始化它们;
• 构造函数最好使用成员初始化列表,而不要在构造函数本体内使用赋值操作。初始化列表列出的成员变量,其排列次序应该和它们在类中的声明次序相同;
• 为免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象。