建议25:尽量用const、enum、inline替换#define(2)
而在GCC编译器中,内置的float、double类型的静态成员常量都可以采用类内初始化,如下所示:
- /* Gcc 4.3 */
- // CMathConstants声明文件(.h)
- class CMathConstants
- {
- private:
- static const double PI = 3.1415926;
- };
当然,如果不习惯类内初始化,讨厌其破坏了静态成员常量声明、定义的统一形式,可以选择将类内初始化全部搬到类实现文件中去,这也是我们比较推荐的形式。更何况早期的编译器可能不接受在声明一个静态的类成员时为其赋初值,那又何必去惹这些不必要的麻烦呢?
另外,如果编译器不支持类内初始化,而此时类在编译期又恰恰需要定义的成员常量值,身处如此左右为难的境地,我们该采取怎样的措施?那就求助于enum!巧用enum来解决这一问题。这一技术利用了这一点:枚举类型可以冒充整数给程序使用。代码如下所示:
- // CStudent声明文件(.h)
- class CStudent
- {
- private:
- enum{ NUM_LESSONS = 5 };
- int scores[NUM_LESSONS];
- };
需要说明的一点是,类内部的静态常量是绝对不可以使用#define来创建的,#define的世界中没有域的概念。这不仅意味着#define不能用来定义类内部的常量,同时还说明它无法为我们带来任何封装效果。
#define的另一个普遍的用法是“函数宏”,即将宏定义得和函数一样,就像建议4中的:
- #define ADD( a, b ) ((a)+(b))
- #define MULTIPLE( a, b ) ((a)*(b))
这样的“函数宏”会起到“空间换时间”的效果,用代码的膨胀换取函数调用开销的减少。这样的宏会带来数不清的缺点,建议4中已经说得很清晰。如果使用宏,必须为此付出精力,而这是毫无意义的。幸运的是,C++(www.cppentry.com)中的内联函数给我们带来了福音:使用内联函数的模板,既可以得到宏的高效,又能保证类型安全,不必为一些鸡毛蒜皮的小问题耗费宝贵的精力。- template<typename T>
- inline T Add(const T& a, const T& b)
- {
- Return (a+b);
- }
-
- template<typename T>
- inline T Multiple(const T& a, const T& b)
- {
- Return (a*b);
- }
这一模板创建了一系列的函数,方便高效,而且没有宏所带来的那些无聊问题。与此同时,由于Add和Multiple都是真实函数,它也遵循作用域和访问权的相关规则。宏在这个方面上确实是望尘莫及。
虽然建议尽量把工作交给编译器而非预处理器,而且C++(www.cppentry.com)也为我们提供了足以完全替代#define的新武器,但是预处理器并未完全退出历史舞台,并没有完全被抛弃。因为#include在我们的C/C++(www.cppentry.com)程序中依旧扮演着重要角色,头文件卫士#ifdef/#ifndef还在控制编译过程中不遗余力地给予支持。但是如果将来这些问题有了更加优秀的解决方案,那时预处理器也许就真的该退休了。
请记住:
对于简单的常量,应该尽量使用const对象或枚举类型数据,避免使用#define。对于形似函数的宏,尽量使用内联函数,避免使用#define。总之一句话,尽量将工作交给编译器,而不是预处理器。