【问题标题】:Why #define is bad? [duplicate]为什么#define 不好? [复制]
【发布时间】:2011-11-25 14:40:26
【问题描述】:

可能重复:
When are C++ macros beneficial?
Why is #define bad and what is the proper substitute?

有人告诉我#define 不好。好吧,老实说,我不明白为什么它不好。如果它不好,那我还有什么办法可以做到这一点呢?

#include <iostream>
#define stop() cin.ignore(numeric_limits<streamsize>::max(), '\n');

【问题讨论】:

标签: c++ c iostream c-preprocessor


【解决方案1】:

#define 并不是天生的。但是,通常有更好的方法来做你想做的事。考虑一个inline 函数:

inline void stop() {
    cin.ignore(numeric_limits<streamsize>::max(), '\n');
}

(真的,你甚至不需要inline 来实现这样的功能。只是一个普通的普通功能就可以了。)

【讨论】:

  • 内联函数?我还没有了解这一点,我正在尝试使用 int 函数和 void。谢谢。 :D
【解决方案2】:

这很糟糕,因为它是不分青红皂白的。代码中包含 stop() 的任何地方都将被替换。

解决问题的方法是将代码放入它自己的方法中。

【讨论】:

    【解决方案3】:

    在 C++ 中,使用 #define 并不是很糟糕,尽管应该首选替代方案。有一些上下文,例如include guards,其中没有其他可移植/标准替代方案。

    应该避免这种情况,因为C preprocessor 在编译器之前运行(顾名思义)。它执行简单的文本替换,而不考虑其他定义。这意味着输入到编译器的结果有时没有意义。考虑:

    // in some header file.
    #define FOO 5
    
    // in some source file.
    int main ()
    {
        // pre-compiles to: "int 5 = 2;"
        // the compiler will vomit a weird compiler error.
        int FOO = 2;
    }
    

    这个例子可能看起来微不足道,但真实的例子是存在的。一些 Windows SDK 标头定义:

    #define min(a,b) ((a<b)?(a):(b))
    

    然后代码如下:

    #include <Windows.h>
    #include <algorithm>
    int main ()
    {
        // pre-compiles to: "int i = std::((1<2)?(1):(2));"
        // the compiler will vomit a weird compiler error.
        int i = std::min(1, 2); 
    }
    

    如果有替代品,请使用它们。在发布的示例中,您可以轻松编写:

    void stop() {
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
    }
    

    对于常量,使用真正的 C++ 常量:

    // instead of
    #define FOO 5
    
    // prefer
    static const int FOO = 5;
    

    这将保证您的编译器看到与您所做的相同的事情并且如预期的那样通过嵌套范围中的名称覆盖(本地 FOO 变量将覆盖全局 FOO 的含义)使您受益.

    【讨论】:

      【解决方案4】:

      不一定不好,只是人们过去使用它的大多数事情都可以以更好的方式完成。

      例如,您提供的 sn-p(和其他代码宏)可能是内联函数,类似于(未经测试):

      static inline void stop (void) {
          cin.ignore(numeric_limits<streamsize>::max(), '\n');
      }
      

      此外,代码宏会迫使您为其他所有事情做“宏体操”,例如,如果您想调用写得很糟糕的代码:

      #define f(x) x * x * x + x
      

      与:

      int y = f (a + 1);  // a + 1 * a + 1 * a + 1 + a + 1 (4a+2, not a^3+a)
      int z = f (a++);    // a++ * a++ * a++ + a++
      

      由于运算符的优先级,第一个结果会让您完全惊讶,第二个会给您未定义的行为。内联函数不会遇到这些问题。

      宏的另一个主要用途是提供枚举值,例如:

      #define ERR_OK    0
      #define ERR_ARG   1
      : :
      #define ERR_MEM  99
      

      这些最好用枚举来完成。

      宏的主要问题是替换是在翻译阶段的早期完成的,因此信息经常丢失。例如,调试器通常不知道ERR_ARG,因为它早在翻译过程中创建调试信息的部分之前就已经被替换了。

      但是,在对它们进行了充分的诽谤之后,它们仍然可用于定义可用于条件编译的简单变量。这就是我现在在 C++ 中使用它们的全部内容。

      【讨论】:

      • 哦...我喜欢你在f(a+1) 上的例子。我到处都看到过a++ 的例子,但从来没有和例子处理过操作顺序:)
      【解决方案5】:

      #define 本身并不坏,但它确实有一些不好的属性。我将列出一些我知道的事情:

      “函数”未按预期运行。

      下面的代码看起来很合理:

      #define getmax(a,b) (a > b ? a : b)
      

      ...但是如果我这样称呼它会发生什么?:

      int a = 5;
      int b = 2;
      int c = getmax(++a,b);    // c equals 7.
      

      不,这不是错字。 c 将等于 7。如果您不相信我,请尝试一下。光是这一点就足以吓到你了。

      预处理器本质上是全局的

      每当您使用#define 定义函数(例如stop())时,它都会在被发现后作用于所有包含的文件。

      这意味着您实际上可以更改不是您编写的库。只要他们在头文件中使用stop()函数,你就可以改变你没有编写和修改的代码的行为。

      调试比较困难。

      在代码进入编译器之前,预处理器会进行符号替换。因此,如果您有以下代码:

      #define NUM_CUSTOMERS 10
      #define PRICE_PER_CUSTOMER 1.10
      
      ...
      
      double something = NUM_CUSTOMERS * PRICE_PER_CUSTOMER;
      

      如果该行有错误,那么您将不会在错误消息中看到方便的变量名称,而是会看到如下内容:

      double something = 10 * 1.10;
      

      因此,在代码中查找内容变得更加困难。在这个例子中,它看起来并没有那么糟糕,但是如果你真的养成了这样做的习惯,那么你可能会遇到一些真正的头痛。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-25
        • 1970-01-01
        • 2011-09-20
        • 2019-08-09
        • 2013-06-01
        相关资源
        最近更新 更多