使用宏有什么问题?

宏不遵循C++中关于范围和类型的规则。这经常导致一些微妙的或不那么微妙的问题。因此,C++提供更适合其他的 C++(译注:原文 the rest of C++,当指 C++除了兼容 C 以外的部分)的替代品,例如内联函数、模板与名字空间。 
  
考虑一下: 
  
    #include "someheader.h" 
  
    struct S { 
        int alpha; 
        int beta; 
    }; 
  
如果某人(不明智地)地写了一个叫“alpha”或“beta”的宏,那么它将不会被编译,
或者被错误地编译,产生不可预知的结果。例如,“someheader.h”可能包含: 
  
    #define alpha 'a' 
    #define beta b[2] 
  
将宏(而且仅仅是宏)全部大写的习惯,会有所帮助,但是对于宏并没有语言层次上的保护
机制。例如,虽然成员的名字包含在结构体的内部,但这无济于事:在编译器能够正确地辨
别这一点之前,宏已经将程序作为一个字符流进行了处理。顺便说一句,这是 C 和 C++程
序开发环境和工具能够被简化的一个主要原因:人与编译器看到的是不同的东西。 
  
不幸的是,你不能假设别的程序员总是能够避免这种你认为“相当白痴”的事情。例如,最
近有人报告我,他们遇到了一个包含 goto 的宏。我也见过这种情况,而且听到过一些——
在很脆弱的时候——看起来确实有理的意见。例如: 
  
    #define prefix get_ready(); int ret__ 
    #define Return(i) ret__=i; do_something(); goto exit 
    #define suffix exit: cleanup(); return ret__ 
  
    void f() 
    { 
        prefix; 
        // ... 
        Return(10); 
        // ... 
        Return(x++); 
        //... 
        suffix; 
    } 
  
作为一个维护的程序员,就会产生这种印象;将宏“隐藏”到一个头文件中——这并不罕见
——使得这种“魔法”更难以被辨别。 
  
一个常见的微妙问题是,一个函数风格的宏并不遵守函数参数传递的规则。例如: 
  
    #define square(x) (x*x) 
  
    void f(double d, int i) 
    { 
        square(d);  // 好 
        square(i++);    // 糟糕:这表示 (i++*i++) 
        square(d+1);    //糟糕:这表示(d+1*d+1); 也就是 (d+d+1) 
        // ... 
    } 
  
“d+1”的问题,可以通过在“调用”时或宏定义时添加一对圆括号来解决: 
  
    #define square(x) ((x)*(x)) /*这样更好 */ 
  
但是, i++被执行了两次(可能并不是有意要这么做)的问题仍然存在。 
  
是的,我确实知道有些特殊的宏并不会导致 C/C++预处理宏这样的问题。但是,我无心去
发展 C++中的宏。作为替代,我推荐使用 C++语言中合适的工具,例如内联函数,模板,
构造函数(用来初始化),析构函数(用来清除),异常(用来退出上下文环境),等等。 

posted on 2007-03-24 09:53 阿刚 阅读(308) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

统计

常用链接

留言簿(1)

随笔档案

文章档案

C++ BBS

C++ FAQ

C++ WEBSITE

搜索

最新随笔

最新评论

阅读排行榜

评论排行榜