尽量以const、enum、inline替换#define

简介: 尽量以const、enum、inline替换#define

34.jpg

1.尽量用const代替#define



1#define ASPECT_RATIO 1.643


记号ASPECT_RATIO可能从未被编译器看见,这是因为宏展开在预处理阶段展开,预处理后的编译阶段它已经不存在了。记号ASPECT_RATIO有可能没有进入记号表内,于是当你运用此常量时得到一个提及1.643的编译错误信息时,可能会引起困惑。当记号ASPECT_RATIO不是被定义在你所写的头文件中,你会对1.643毫无概念。


解决上面问题的方法是:用一个常量替换上述的宏。


1const double AspectRatio = 1.643  // 注意:全部大写名称常用于宏


作为语言常量(并非是字面常量,语言常量实质是一个变量,只不过它不能被修改而已),AspectRatio一定会被编译器看见,当然会进入记号表中。就上例来说,使用常量可能比使用#define导致较小量的码,因为宏展开时,预处理器会盲目地将记号ASPECTRATIO替换为1.643,可能导致目标码出现多份1.643。


当使用const替换#define时,有两种特殊情况值得研究一下:

(1).定义常量指针

(2).类class的专属常量


(1).定义常量指针


由于常量定义式通常被放在头文件中,便于被不同的源码包含。因此有必要将指针(不仅仅是指针所指之物)声明为const。此时,就需要定义一个指向常量的常指针,它的作用是类似于用宏定义了一个常量。如下例所示:


1const char * const authorName = "CurryCoder";
1// 常量指针
2const int *p;
3int const *p;
4
5// 指针常量
6int * const p;
7
8// 指向常量的常指针
9const int* const p;  // 此句等同于定义了一个常量 const int IntApp = p;


(2).class专属常量


为了将常量的作用域限制在一个类体内,必须让它成为这个类的一个成员。同时,为了确保此常量至多只有一个实体,必须让它成为一个static成员。如下例所示:


1class Player{
2private:
3   static const int NumTurns = 5;
4   int scores[NumTurns];
5   ....
6};


上例中,你所看到的是NumTurns的声明式而非定义式,通常C++要求你对你所使用的任何东西提供一个定义式,如果它是一个class专属常量又是static且为整数类时,需要特殊处理。只要你不取它们的地址,你可以声明并使用它而无需提供定义式。但如果你取某个class专属常量的地址,或者即使你取其地址而你的编译器却要坚持看到一个定义式,你就必须另外提供定义式:


1const int Player::NumTurns;   // 定义式


一定要将上面的定义式放入一个实现文件中,而非是头文件中。因为class常量已经在声明式中获得了初始值。于是,定义式不可以再设置初值。


注意:无法利用#define创建一个class专属的常量,因为宏没有作用域的概念,不能提供任何封装性。一旦宏被定义,它就在其后的编译过程中有效(除非在某处被#undef)。


旧的编译器也许不允许static成员在其声明式上获得初值。在类的内部设置static数据成员的初始值,也仅仅只允许对整型常量允许。如果你的编译器不支持上述语法,可以在类的外部进行进行static数据成员的初始化(通常情况下,也采用这种方式进行静态数据成员的初始化)。如下例所示:


1class Person{
 2private:
 3    string name;
 4    static const int age = 28;   // 只有const int静态数据成员才能在声明式上获得初始值
 5    static const string address; // const string静态数据成员不可以在声明式上获得初始值,只能在类外进行初始化
 6    // static const int numTurns = 5;
 7    enum {numTurns = 5};  // 使用枚举类型代替类内进行初始化
 8    int scores[numTurns] = {99, 93, 90, 84, 73};
 9
10
11
12public:
13    Person(const string& nm): name(nm){}
14    static string PrintAddress(){
15        return address;
16    }
17
18    void Print(){
19        cout << name << " is " << age; 
20    }
21};
22
23const string Person::address = "NanJing";


2.用enum代替const


只有一种例外的情况:当你在class编译期间需要一个class常量值。例如,上述的Player::scores的数组声明中,编译器坚持必须在编译期间知道数组的大小。此时,如果你的编译器不允许static const int NumTurns = 5;,可以改用所谓的the enum hack补偿做法。如下例所示:


1class Player{
2private:
3   enum {
4     NumTurns = 5;
5   };
6   int scores[NumTurns];
7   ....
8};


enum hack的行为某方面来说比较像#define而不像const,因为取一个const的地址是合法的(const变量实际也是变量,它只不过不能被修改而已),但取一个enum的地址是不合法的(enum类型中的枚举项只是类型声明的一部分,不是定义出来的变量),而取一个#define的地址也是不合法的(它是预处理的东西,预处理后的编译阶段它已经不存在)。如果你不想让别获得一个pointer或referencce指向你的某个整型常量,enum可以帮你实现这个约束。


3.尽量用enum代替#define


常见的#define误用的情况是以它实现宏。宏看起来像函数,但不会招致函数调用带来的额外开销。下例中这个宏夹带着宏实参,调用函数f。当你写成下面的宏时,必须记住为宏中的所有实参加上小括号。否则,别人在表达式中调用这个宏时可能会很麻烦。


1#define CALL_WITH_MAX(a, b) f((a) > (b)) ? (a) : (b))
2
3int a = 5, b = 0;
4CALL_WITH_MAX(++a, b);
5CALL_WITH_MAX(++a, b+10);  // 函数f之前,a的递增次数取决于它被拿来与谁比较


解决上述问题,只需写出template inline函数模板即可。它不需要在函数内部中为参数加上小括号,也不需要担心参数被更改。如下例所示:


1template<typename T>
2inline void callWithMax(const T& a, const T& b){
3    f(a > b) ? a : b;
4}


4.总结


有了const、enum和inline我们对预处理器的需求降低了,但并非完全消除。#include仍然是必需品,而#ifdef/#ifndef也继续扮演控制编译的重要角色。请记住:

(1).对于单纯常量,最好以const对象或enums替换#define

(2).对应形似函数的宏,最好改用inline函数替换#define

相关文章
container_of(ptr,type,member)宏
详细解释了container_of(ptr,type,member)宏的用途
#define的用法
在程序中扩展#define定义符号和宏时,需要涉及几个步骤。 1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先 被替换。 2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。 3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
124 0
|
6月前
|
人工智能 安全 机器人
【C++】const_cast基本用法(详细讲解)
【C++】const_cast基本用法(详细讲解)
106 0
|
编译器
#define 与 const 区别
#define 与 const 区别。
60 1
const与#define的区别与联系
const与#define的区别与联系
111 0
【C++】 --- C++函数后面加const修饰符
【C++】 --- C++函数后面加const修饰符
104 0
用#define宏实现Add函数
用#define宏实现Add函数
102 0
|
存储 数据库 开发者
extern,const,#define
extern,const,#define
74 0
|
编译器 C++
Effective C++条款 02:尽量以 const, enum, inline 替换 #define
Effective C++条款 02:尽量以 const, enum, inline 替换 #define
135 0
|
存储 安全 编译器
C++:宏定义(#define)和常量(const)的区别
在测试对矩阵进行 SVD 分解时,需要定义矩阵的行和列的大小,下面我们开始思考宏定义和常量之间有些什么样的分别。
617 0
C++:宏定义(#define)和常量(const)的区别