12

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

这是一个示例(未经测试的代码,但您大致了解):

#define VERSION "1.2"

#include <string>

class Foo {
  public:
    string getVersion() {return "The current version is "+VERSION;}
};
  1. 为什么这段代码不好?
  2. 有没有使用的替代方法#define
4

5 回答 5

14

为什么这段代码不好?

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

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

const char * VERSION = "1.2";

或者

const std::string VERSION = "1.2";
于 2012-04-21T18:40:43.163 回答
11

真正的问题是定义是由与语言的其余部分(预处理器)不同的工具处理的。结果,编译器对此一无所知,并且在出现问题时无法帮助您——例如重用预处理器名称。

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

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

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

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

“正确地”做这件事本身就是一门艺术,但有一些简单的指导方针:

  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))
    

但是仍然有很多方法可以让宏严重错误,而且,重申一下,编译器在这里帮不了你。

于 2012-04-21T18:50:05.433 回答
4

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

于 2012-04-21T18:40:44.240 回答
2

我不会#define用来定义一个常量使用static关键字或更好 的const int kMajorVer = 1; const int kMinorVer = 2; 或者 const std::string kVersion = "1.2";

Herb sutter 在这里有一篇很棒的文章详细说明了为什么#define不好,并列出了一些确实没有其他方法可以实现相同目标的示例:http ://www.gotw.ca/gotw/032.htm 。

基本上就像许多事情一样,只要你正确使用它就可以了,但它很容易被滥用,而且宏错误特别神秘,而且是调试的错误。

我个人将它们用于条件调试代码以及​​变体数据表示,这在 sutter 文章的末尾进行了详细说明。

于 2012-04-21T19:00:23.060 回答
1

一般来说,预处理器很糟糕,因为它创建了一个不安全的两遍编译过程,产生了难以解码的错误消息,并且可能导致难以阅读的代码。如果可能,您不应该使用它:

const char* VERSION = "1.2"

但是,在某些情况下,如果没有预处理器就不可能做你想做的事情:

#define Log(x) cout << #x << " = " << (x) << endl;
于 2012-04-21T18:44:35.777 回答