【问题标题】:Why is #define bad and what is the proper substitute?为什么#define 不好,什么是正确的替代品?
【发布时间】:2011-01-17 17:10:12
【问题描述】:
#define dItemName        L"CellPhone"

【问题讨论】:

标签: c++


【解决方案1】:

将其定义为常量变量。这是一种很好的编程习惯。

const wchar_t *dItemName = L"CellPhone";

如果您以后需要知道字符串的长度,则将其定义为数组:

const wchar_t dItemName[] = L"CellPhone";

另外,为什么#define 不好:它将所有使用单词 dItemName 的地方转换为 L"CellPhone"。示例:

struct {
  int dItemName;
} SomeStruct;

将失效:

struct {
  int L"CellPhone";
} SomeStruct;

【讨论】:

  • 好吧,文字已经是强类型的了,但是唉。您缺少一个 *。
  • 您忘记了*std::wstring
  • 嗯...只是为了挑剔...文字实际上不是指针,而是数组,所以正确的定义是:const wchar_t dItemName[] = L"CellPhone";。几乎没有区别,但有一些区别。特别是,编译器知道数组的大小,但不知道指针指向的元素数量。
【解决方案2】:

有趣的是,我找不到一个问题来指出所有的缺点,甚至这个主题之前肯定已经讨论过。

首先,在C(不是C++)中,这是声明常量的方式。这也解释了为什么这么多 C++ 开发人员仍在使用它:当他们来自 C 背景或曾被具有 C 背景的人教过/学习过时,他们往往会重现这种 C-ish 行为。

然而,在 C++ 中,我们有更好的工具。

#define 没有定义常量,它定义了一个宏

  1. 宏不知道范围
  2. 宏不是类型安全的

宏不知道作用域:

它们是预处理工具:预处理器不了解底层语言的规则(无论是 asm、C 还是 C++),并且总是会扩展它所拥有的符号而不考虑范围。

因此,通常建议使用一组特定的符号来区分宏。人们通常使用ALL_CAPS 符号,但您需要记住:

  • 它们不应包含两个连续的下划线
  • 它们不应以下划线开头

为了符合 C++ 标准。

宏不是类型安全的。

正如我所说,预处理器忽略了底层语言规则,因此以下内容并不奇怪:

#define FOO "foo"

int main(int argc, char* argv[])
{
  if (FOO) { ... }

  return 0;
}

另一方面,使用正确的类型可以防止这种无意的错误:

std::string const Foo = "foo";

结论?

如果你愿意,你可以使用#define,只是你做额外的工作而不是编译器,但这是你的决定。个人:我很懒:)

【讨论】:

  • 我是一名 obj-c 开发人员,最近我开始将常量转换为常量变量(参见 Al Kepp 的回答),这些变量最初是定义的宏。如果不是为了代码执行和常量定义,#define 有什么用处吗?(尤其是,但不是专门针对 obj-c)
  • @YunusNedimMehel:我的主要用例是记录宏。如果你想在调用日志的地方访问__FILE__/__LINE__,宏是必要的,因为函数,即使是内联的,也会使用它们定义的__FILE__/__LINE__,而不是从点-使用。类似地,对于 C(在带有 RAII 和异常的 C++ 中不那么重要),对函数中的清理部分的名称有一个项目范围的约定,带有检查返回的宏和goto 清理部分不是糟糕(替代方案是箭头模式),并且只有宏可以goto 在使用点范围内。
【解决方案3】:

#define 的一个主要问题是它在语言本身之外,因此不限于给定范围。您将在翻译单元中的任何位置、所有命名空间、类、函数等中替换 dItemName

我会用const std::wstring dItemName = L"CellPhone";替换它

【讨论】:

    【解决方案4】:

    #define 是定义宏的预处理器指令。在您的情况下,宏 dItemName 具有值 L"CellPhone"

    宏不好主要是因为它们在实际代码之前被处理。这意味着它们不受范围和 C++ 语法规则的约束。如果你在某处有一个名为 dItemName 的变量,那么事情可能不会起作用:因此你会遇到难以理解的编译错误。

    解决方案是将dItemName 声明为变量(在本例中为const 变量)。

    【讨论】:

      【解决方案5】:

      指出将做他们发明的事情定义为失败是-将切割归咎于刀。

      如果你没有正确地用 UPPERCASE_NAMES 命名你的定义,你会遇到麻烦,但是如果你不能自律你的工作方式,那么在 C 中你会遇到这些麻烦。

      您不能使用 const 生成动态重新排列系统,因此它不适合任何为使用而定制的预编译的嵌入式应用程序。Const 只能分配预评估常量,因此甚至不能分配其他 const 表达式。

      工具不会因为不服从 OO 范式而突然变得无用。就功能而言,Const 不等于替换。

      【讨论】:

        【解决方案6】:

        因为您刚刚创建的预处理器宏会污染每个名称范围。它们随处可用,并且不遵循标准命名范围规则。因此,使用这样的宏,int dItemName = 5; 之类的代码会被预处理器处理为int L"CellPhone" = 5;。一个常量的全局变量不会这样做。

        抛开所有其他问题不谈,这是 IMNSHO 宏定义方面最糟糕的问题。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-02-15
          • 1970-01-01
          • 2016-08-30
          • 2013-07-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多