在C++中使用"#define"的替代方法?为什么不赞成?

Joe*_*oel 10 c++ coding-style global-variables c-preprocessor

我已经开发了不到一年的C++,但在那段时间里,我听到很多人都在谈论有多糟糕#define.现在,我意识到它是由预处理器而不是编译器解释的,因此无法调试,但这真的那么糟糕吗?

这是一个例子(未经测试的代码,但你得到了一般的想法):

#define VERSION "1.2"

#include <string>

class Foo {
  public:
    string getVersion() {return "The current version is "+VERSION;}
};
Run Code Online (Sandbox Code Playgroud)
  1. 为什么这个代码不好?
  2. 有没有替代使用#define

Ben*_*ley 11

为什么这个代码不好?

因为VERSION可以被覆盖,编译器也不会告诉你.

有没有使用#define的替代方案?

const char * VERSION = "1.2";
Run Code Online (Sandbox Code Playgroud)

要么

const std::string VERSION = "1.2";
Run Code Online (Sandbox Code Playgroud)

  • 实际上,编译器可能*会在重新定义宏时告诉您.即使没有任何警告选项,gcc也会警告你"警告:"FOO"重新定义......注意:这是前一个定义的位置".只有当值不同时才会这样做,所以你不会得到无用的警告. (4认同)
  • @Joel:`const x`优于`#define`的最大优点之一是它的作用域. (3认同)

Kon*_*lph 10

真正的问题是定义是由与其他语言(预处理器)不同的工具处理的.因此,编译器不知道它,并且在出现问题时无法帮助您 - 例如重用预处理器名称.

考虑max有时将其实现为宏的情况.因此,您无法max在代码中的任何位置使用标识符.任何地方.但编译器不会告诉你.相反,你的代码会出现严重错误,你不知道为什么.

现在,小心一点,这个问题可以最小化(如果没有完全消除).但是对于大多数用途而言,#define无论如何都有更好的替代方案,因此成本/收益计算变得偏差:轻微劣势,无论如何没有任何好处.为什么在没有优势的情况下使用有缺陷的功能?

所以这是一个非常简单的图表:

  1. 需要一个常数?使用常量(不是定义)
  2. 需要一个功能?使用函数(不是定义)
  3. 需要一些无法使用常量或函数建模的东西?使用define,但要正确执行.

"正确"地做它本身就是一门艺术,但有一些简单的指导方针:

  1. 使用唯一的名称.所有大写字母,始终以唯一的库标识符为前缀.max?出.VERSION?出.相反,使用MY_COOL_LIBRARY_MAXMY_COOL_LIBRARY_VERSION.例如,Boost库,宏的大用户,总是使用以BOOST_<LIBRARY_NAME>_.开头的宏.

  2. 谨防评估.实际上,宏中的参数只是被替换的文本.因此,#define MY_LIB_MULTIPLY(x) x * x它被破坏了:它可以被用作MY_LIB_MULTIPLY(2 + 5),导致2 + 5 * 2 + 5.不是我们想要的.为了防止这种情况发生,总是充分利用这些论点的所有用法(除非你确切地知道你在做什么 - 扰流板:你可能不会这样做;甚至专家也常常惊慌失措).

    这个宏的正确版本是:

    #define MY_LIB_MULTIPLY(x) ((x) * (x))
    
    Run Code Online (Sandbox Code Playgroud)

但是仍然有很多方法可以让宏发生可怕的错误,重申一下,编译器在这里不会帮助你.


Kyl*_*yle 5

#define本质上并不是坏事,只是很容易被滥用。对于像版本字符串这样的东西,它工作得很好,虽然 aconst char*会更好,但许多程序员使用它的用途远不止于此。#define例如,使用typedef 是愚蠢的,而在大多数情况下,typedef 会更好。所以声明并没有什么错#define,有些事情没有声明就做不成。必须根据具体情况对它们进行评估。如果您能够找到一种不使用预处理器来解决问题的方法,那么您应该这样做。