一. 内容
-
这个条款或许改为
宁可编译器替换预处理器
比较好。 -
定义常量指针
有必要将指针(而不止是所指之物)声明为 const。
例如定义一个常量的 char*-based 的字符串,必须 const 两次。前一次是类型需要,后一次限制为常量。const char* const Author="somebody"; FFunctionLibrary::Println(Author);//somebody Author="anybody";//error:类型只可读 FFunctionLibrary::Println(Author);//anybody
当然使用 std::string 替换 const char* 更合适。
-
为了将常量的作用域(scope)限制在class内,你得让它成为 class 的一个成员(member),而为了确保常量至多只有一份实体。你必须让它成为一个static成员。(Moota:只是需求,不是常量必须为静态)
class ANonDefine { public: static const int ConstNumber=10; };
但是你所看到的只是 ConstNumber 的声明式,而非定义式。通常情况下C++会要求对你所使用到的任何东西提供一个定义式,但也有特殊情况。
如果它是个class专属常量又是static且为整数类型(例如ints,chars,bools),只要不取它们的地址,可以直接声明并使用它们。除非如果坚持要取地址或者你的编辑器强制要求看到一个定义式,
class ANonDefine { public: static const int ConstNumber=10; }; const int ANonDefine::ConstNumber=9;
注意上面的写法是错误的。
由于 static 只能定义被初始化一次,请把定义式放在实现(.cpp)文件
而非头(.h)文件。
由于常量已在声明时获得初值
,因此在定义它的时候不可以再设初值。顺带一提的是我们无法利用 #define 去创建一个 class 专属常量,因为
#define并不重视作用域
。一旦宏被定定义,它将在之后的编译过程中都有效(除非在某处被 #undef )。这也意味宏没有任何封装性,也就是指没有private #define,public #define。 -
当使用常量作为数组大小时,如下的代码是错误的
class ANonDefine { public: static const int ConstNumber; int Array[ConstNumber]; };
因为编辑器坚持在编译期知道数组的大小。
解决方法
- 在ConstNumber声明时就赋值。
- 使用enum
class ANonDefine { public: enum {ArraySize=5}; int Array[ArraySize]; };
其理论基础是:
一个属于枚举类型的数值可以充当ints被使用。
enum hack 的行为某方面来说比较像 #define 而不是const,有时候这就是想要的,例如取一个 const 的地址是合法的,而取一个 enum 的地址是非法的,而取一个#define 的地址通常也不合法。如果不想别人指针或者引用自己某个整形常量,enum 可以实现这个约束。 -
#define容易误用
比如宏函数#define Add(a,b) a+b int main() { FFunctionLibrary::Println(Add(1,2)*Add(3,4));//1 system("Pause"); return 0; }
Add 宏函数执行 a+b 的行为,但宏只是文本替换,像上述的代码实际执行的是 1+2 * 3+4=11,而不是我们期望的(1+2)*(3+4)。这也就是说,
你必须记住为宏中的所有实参加上小括号。
-
inline 而对于替换行为,更好的选择是使用 inline 函数(内联)。
template <typename T> inline T AddUsingInline(const T& a,const T& b) { return a+b; }
-
有了const,enum,inline,我们对于预处理器(特别是 #define)的需求降低了,但并非完全消除。#include 仍然是必需品,而 #ifdef,#ifndef 也继续扮演着控制编译的重要角色。目前还没到预处理器全面退出的时候,但你应该明确地慎用它。
二. 总结
- 对于单纯常量,最好是以const对象或者enums替换#define。
- 对于形似函数的宏(macros),最好改用inline函数替换#defines。