条款01:视C++为一个语言联邦
为了更好的理解C++,我们将C++分解为四个主要次语言:
· C。说到底C++仍是以C为基础。区块,语句,预处理器,内置数据类型,数组,指针统统来自C。
· Object-Oreinted C++。这一部分是面向对象设计之古典守则在C++上的最直接实施。类,封装,继承,多态,virtual函数等等...
· Template C++。这是C++泛型编程部分。
· STL。STL是个template程序库。容器(containers),迭代器(iterators),算法(algorithms)以及函数对象(function objects)...
请记住:
- 这四个次语言,当你从某个次语言切换到另一个,导致高效编程守则要求你改变策略。C++高效编程守则视状况而变化,取决于你使用C++的哪一部分。
- C++并不是一个带有一组规则的一体语言;它是从四个语言组成的联邦政府,每个语言有自己的规约。记住这四个次语言你就会发现C++容易了解得多。
条款02:尽量以const,enum,inline来替换const。
这个条款或许改为“宁可以编译器替换预处理器”更好。
因为预处理器可能往往会带来一些不好的结果,,
例如这样写:
#define A_FLOAT_NUMBER 1.234
实际上A_Float_Number 并未被编译器看见,编译器只看见了1.234。
所以一旦因为我们运用这个常量而导致编译器在某个地方报出了错误,错误信息可能会提到1.234而不是具体指出 A_Float_Number ,如果A_Float_Number被定义在一个非我们所写的头文件中,我们肯定不知道 1.234是个啥玩意儿,然后会因为追踪它而浪费时间。这个问题也可能出现在记号式调试器(symbolic debugger) 中,原因相同:所使用的名称很可能未进入记号表(symbol table)。
比较好的替换方法,是使用一个常量替换上述的宏(# define):
const double A_float_number = 1.234; //通常宏权为大写,而这里小写
使用常量可能比使用#define 导致更小的代码量,因为预处理器“盲目的将每一处A_FLOAT_NUMBER 都替换为 1.234 ” 而导致目标代码出现多份1.234 ,而改用常量写法,就保证仅有一份1.234出现在代码里。
class专属常量:
class GamePlayer{
private:
static const int NumCounts = 5;
int scores[NumCounts];
...
};
注意:然而有些编译器不允许“类内(in-class)初值设定”,且这种设定仅限对于整数常量执行。如果我们的编译器不支持这种语法,可以在类内声明,类外初始化:
class GamePlayer{
rivate:
static const int NumCounts ; //类内声明
int scores[NumCounts];
};
const int GamePlayer ::NumCounts = 5; //类外初始化。
唯一的例外,是当我们在class编译期间需要一个class常量值,例如在上述的GamePlayer::scores的数组声明式中(如果编译器坚持必须在编译期间直到数组的大小),而这时候万一你的编译器(错误的)不允许class的静态常量成员完成“iin-class”初值设定,则可改用所谓的“the enum hack”补偿做法。其理论基础是:“一个属于枚举类型的数值可权充int被使用”,于是GamePlayer可被定义如下:
class GamePlayer{
private:
enum { NumCounts = 5 } ;
int scores[NumCounts];
...
};
enum比较安全,取一个const的地址是合法的,但是取一个enum的地址就不合法,而取一个#define的地址通常也不合法。如果你不想让别人获得一个 poniter 或 reference 指向你的某个整型常量,enum可以帮助你实现这个约束。
认识enum hack的理由是纯粹的实用主义。许多代码使用了它,事实上,“enum hack”是模板元编程(template metaprogramming)的基础技术。
用inline替代#define
下面这个宏夹带着实参,调用函数f();
#define CALL_WITH_MAX(a,b) f( (a)>(b)? (a) : (b) ) //这种宏缺点太多
应该使用inline实现功能来替代宏:
template<typename T>
inline void callWithMax(cosnt T &a, cosnt T &b)
{
f(a > b ? a : b);
}
这里,callWithMax是一个真正的函数,遵循作用域(scope)和访问规则。例如我们可以写出一个“class内的private inline 函数”,而宏无法实现private这种性质。O(∩_∩)O~
请记住:
· 对于单纯常量,最好以const对象或enums替换#defines;
· 对于形似函数的宏,最好改用inline函数替换#defines。