在C语言中,使用宏可以不需要普通的函数调用的代价,使得结果看起来就像函数被调用。宏的实现是使用预处理器而不是编译器。预处理器直接用宏代码代替宏的调动,就可以节省出参数压栈、生成汇编语言的CALL、返回参数、执行汇编语言的RETURN等等开销。
但是在C++中,预处理器的宏存在两个问题:宏看起来像函数调用,但是实际上可能隐藏难以发现的错误,预处理器也不被允许访问成员数据,这意味着预处理器宏无法用作类的成员函数。
1预处理器
使用预处理宏的时候,往往可能出现字符优先级引发的问题,因为预处理器做的仅仅只是替代,尽管我们可以灵活使用括号来避免这些问题,但这仍然不是我们想要的编程方式。
例如上面的例子,我们想要执行取a,b的中的最大值再加2的操作,在使用预处理器宏的时候,结果却不是我们想要的。输出的c是5,而d是7。
这是因为由于预处理器宏的替代作用,实际上第二行代码效果和下面这行代码一样:
int c = a > b ? a : b + 2;
2内联函数
在C++中,宏的概念是作为内联函数来实现的是,内联函数可以像普通的函数一样具有我们希望的任何行为,唯一不同之处在于内联函数在适当的地方像宏一样展开,节省下了函数调用的开销。
任何在类内部定义的函数自动地成为了内联函数,但是也可以在非类的函数前面加上inline关键字使之成为内联函数。但是需要注意,必须将函数体和声明结合在一起,否则将会无效。
例如:
inline int plusOne(int x);
没有任何效果。
inline int plusOne(int x) {return ++x;}
正确的内联函数使用方法如上。
一般应该把内联定义在头文件中,当编译器看到内联的定义,将会把函数类型(函数名+返回值)和函数体放在符号表中,当使用函数的时候,编译器检查来确保调用是正确的并且返回值被正确使用,然后把函数调用替换为函数体,消除开销。
内联代码占用空间,因此需要尽量在较小的函数中使用,如果函数较小,实际上比为了一个普通函数被调用而产生的代码,例如压栈和执行CALL占用的空间还要少。
例如:
需要注意的是,使用内联函数的时候,声明和定义必须在调用之前。内联函数内部不应该使用迭代,不应该使用switch关键字,也不应该包括异常处理的内容,更不能递归。这都是出于使内联函数的代码量尽量小,亦或者是在替换代码时候不出错考虑。
定义内联函数的时候,通常在函数定义的前面加一个inline关键字,但是在类内部定义内联函数的时候,inline关键字并不是必须的,任何在类内部定义的函数会自动成为内联函数。
但是,如果内联函数的缺点一样,如果在类内部的内联函数较大,重复的代码替换会让代码膨胀,我们仍然需要使用在内部声明,在外部定义的方式。