Accustoming yourself to C++
------------------------------------------------------------------------------------
Item2:
Prefer consts, enums, and inlines to #defines.
换句话说: Prefer the compiler to the preprocessor.
------------------------------------------------------------------------------------
Things to Remember
✦ For simple constants, prefer const objects or enums to #defines.
✦ For function-like macros, prefer inline functions to #defines.
-------------------------------------------------------------------------------------
When replacing #defines with constants, two special cases are worth mentioning.
1. Defining constant pointer.
常数的定义通常放在头文件, it's important that the pointer be declared const, usually in addition to what the pointer points to.
To define a constant char*-based string in a header file, for example, you have to write const twice:
const char * const authorName = "Scott Meyers";
String objects are generally preferable to their char*-based progenitors.
A better way:
const std::std authorName("Scott Meyers");
2. Class-specific constants.
To limit the scope of a constant to a class, you must make it a member, and to ensure there's at most one copy of that constant, you must make it a static member:
class GamePlayer{
private:
static const int NumTurns = 5; // constant declaration
int scores[NumTurns]; // use of constant
...
};
--------------------------------------------------------------------------------------------------------------------------------
Usually, C++ requires that you provide a definition for anything you use. But, class-specific constants that are static and of integral type(e.g., integers, chars, bools) are an exception.
As long as you don't take their address, you can declare them and use them without providing a definition.
------------------------------------------------------------------------------------------------------------------------------
如必须定义,可单独定义如下:
const int GamePlayer::NumTurns; //definition of NumTurns;
将这行定义放在应用文件里,而不是头文件里。
The initial value of class constants is provided where the constant is declared, no initial value is permitted at the point of definition.(在声明的时候已经给值了,所以定义的时候不要赋值)
There is no way to create a class-specific constant using #define, because #define don't respect scope.
使用#define不能定义类的常数,因为#define是忽略范围的。
Once a macro is defined, it's in force for the rest of the compilation. Which means that #define can't be used to provide any kind of encapsulation(封装), either.
Sum for #define here:
- Can't be used to define a class-specific constant;不能用以定义类常数;
- Can't be used to provide encapsulation.不能用于封装。
-----------------------------------------------------------------------------------------------------------------
In case the syntax can't be used due to older compilers' limits, you can put it this way:
如果由于编译器版本太老的原因,以上语法不适用,可以如下重新定义类中常数:
class ConstEstimate{
private:
static const double FudgeFactor; //declaration of static class constant; goes in header file
…
};
const double CostEstimate::FudgeFactor = 1.35; //definition of static class constant; goes in impl.file
如果遇到类似的问题,这一段代码你可以直接拿来用。
唯一的例外是,当你需要在类的编译过程中拿到一个类中的常数的值,比如上面的GamePlayer::scores数组的声明(这个时候,编译器在编译过程中必须知道这个常数的大小)。
----------------------------------------------------------------------------------------------------------------------
不过我们有补偿办法,which is known as “the enum hack.”
此方法利用了一个事实:枚举类型的值可以当作int来用。
那么我们可以重新定义GamePlayer:
class GamePlayer{
private:
enum{ NumTurns = 5 }; //"the enum hack" --makes NumTurns a symbolic name for 5
int scores[NumTurns]; //fine
…
};
The enum hack值得你了解的几点原因:
1. The enum hack在某些地方,比一个const表现得更像一个#define。比如,对一个const取址是合法的,但是对一个enum取址是不合法的,对一个#define取址是典型的不合法的。如果你不希望人们得到你某个整型常数的一个指针或者引用,那么你可以使用enum类型来代替这个int constant。再比如,好的编译器不会给整型的const object分配额外的存储空间,但是有的草率的编译器可能会这样做,而你并不想要给这样的object分别额外的内存。和#define一样,enum是不会有这样不必要的内存开销的。
2. The enum hack is a fundamental technique of template metaprogramming(模板元编程). 回到预处理器,另一个#define的误用是,使用#define来实现看起来像函数的宏但不会产生函数调用的开销。下面定义一个取更大值的宏:
//call f with the maximum of a and b
#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自增一次
在这儿,在调用f之前a 的自增次数决定于和a比较的数值b的大小。
幸运的是,上面说的这些你全都可以避免,你只需要使用template来造一个inline function:
template<typename T>
inline void callWithMax(const T&a, const T&b)
{
f(a>b?a:b); //由于我们不知道T是什么,于是 we pass by reference-to const;
} //for more, see Item 20
这个模板产生了一个函数族,每个函数f接受两个类型相同的object,使用其中更大的一个object来调用f。
Given the availability of consts, enums, and inlines, your need for the preprocessor (especially #define) is reduced, but it’s not eliminated. #include remains essential, and #ifdef/#ifndef continue to play important
roles in controlling compilation. It’s not yet time to retire the preprocessor, but you should definitely give it long and frequent vacations.
总结:尽量用consts, enums inlines来取代预处理器的部分功能。
-----------------------------------------------------------------------------------------------------------
Things to Remember
✦ For simple constants, prefer const objects or enums to #defines.
✦ For function-like macros, prefer inline functions to #defines.