您现在的位置是:首页 >学无止境 >解读<<effective c++>> 条款02网站首页学无止境
解读<<effective c++>> 条款02
尽量以const,enum,inline替换 #define
1.先看一条语句:
#define ASPECT_RATIO 1.653
这条语句看上去什么问题,就是一个简单的宏定义,但是实际上它存在着一点问题:
1.我们都知道,当我们在编译代码的时候,编译器会将多个.c文件形成.o文件,然后会将多个.o文件进行链接再形成可执行程序,那么在链接的时候,代码中的各种符号都被分配了地址,然后链接器会将这些写进符号表。
2.上面那条语句的常量ASPECT_RATIO也会被写进符号表吗?
答案是不一定。
因为编译器会将所有的ASPECT_RATIO都用值1.653进行替换,所以这个宏名称也许从未被编译器看见,于是这个宏名称甚至可能没有进入符号表。
3.那么会存在什么问题呢?
既然它没有被记录在符号表里,那么当你运用该宏但获得一个编译错误信息时,可能会带来困惑,因为这个错误信息也许会提到1.653而不是ASPECT_RATIO。如果它被定义在了一个非你所写的头文件里,那么你肯定对这个1.653的来源表示不解,最终也只是浪费时间。而且,宏定义在系统在编译时候,就将其全部替换,而不会对其变量进行类型检查,相对不安全。
所以,我们就需要这样替代#define:
const double AspectRatio = 1.653;
它作为一个语言常量,AspectRatio一定会被编译器看到,也会进入符号表。此外,使用常量可能比使用宏导致较小量的码,因为预处理器会盲目地将宏名称替换为它的值,导致.o文件出现多份1.653,使用const则避免了这些。
2.为了将常量的作用域限制于class内,你必须让它成为一个class的一个成员,而为了确保此常量至多只有一份实体,那么你必须让它成为一个静态成员:
class game
{
private:
static const int num=5;
int scores[num];
};
注意:虽然看到了这个num被赋了一个值5,但是这条语句只是个声明而不是定义
如果取某个class专属常量的地址,或纵使你不取其地址而你的编译器却(不正确地)坚持要看到一个定义式,你就必须另外提供定义式:
const int game::num;
那么此处能不能用#define替换const呢?
不能,因为我们无法用#define去创建一个class专属常量,因为它并不重视作用域。一旦宏被定义,它就在其后的编译过程中有效,这意味着#define不仅不能用来定义class专属常量,也不能提供任何封装性。
注意:
1.static 成员变量属于类,不属于某个具体的对象,即使创建多个对象,也只为 m_total 分配一份内存,所有对象使用的都是这份内存中的数据。当某个对象修改了 m_total,也会影响到其他对象。
2.有些老版的编译器可能不支持在类里面赋值,可以尝试在类外定义的时候来赋值
3.如何解决“不支持在声明的时候设置初始值”的问题?
class A { private: enum {LEN = 5}; int score[LEN]; };
那就用enum来代替。
3.请观察以下代码,找出存在的问题
已知存在:
#define CALL_WITH_MAX(a,b) f((a) > (b) ? (a):(b))
那么:
int a=5,b=0;
CALL_WITH_MAX(++a,b);
CALL_WITH_MAX(++a,b+10);
这种写法会存在什么问题?
你只是想简单地进行两次比较,并且输出两个值的最大的那个,执行宏替换的时候,会f((a++) > (b) ? (a++) : (b) ) 这样a被加了两次。
如何避免?
可以使用inline替换#define
template<typename T> inline void call(const T& a,const T& b) { f(a>b?a:b); }
注意:
1.内联函数可以进行诸如类型安全检查、语句是否正确等编译功能,宏不具有这样的功能;宏不是函数,而inline是函数。
2.inline和宏定义存在类似的功能,就是都避免了函数栈帧的创建和销毁,提高了效率,但是内敛函数并不是万能,它会直接在调用的位置展开,扩大了程序的体积,这样将使程序的总代码量增大,消耗更多的内存空间,所以并不是什么时候都能用内联。
3.内敛函数对编译器来说只是个建议,是否成为内敛还需要编译器自己判断能否成为内联。
4.成员函数默认是内联函数(当然也需要编译器进行判断),如果为了防止程序体积太大,可以进行声明和定义分离
5.宏的缺点:
(1)在预处理阶段被替换,不会进行类型检测,代码安全性低
(2)在预处理阶段展开–>不能调试
(3)每个使用部分都会展开---->造成代码膨胀
(4)容易出错,每个部分需要加括号
请记住:
对于单纯常量,最好以const对象或enums替换#define
对于形似函数的宏,最好改用inline函数替换#define