【问题标题】:Should I avoid using #define in C++? Why, and what alternatives can I use?我应该避免在 C++ 中使用#define 吗?为什么,我可以使用哪些替代品?
【发布时间】:2013-03-05 08:12:00
【问题描述】:

对于某些类型的程序,我需要使用一个恒定的高值来表示某些变量的某些属性。我的意思是让color[i] = 1000000; 如果树中的i 节点未被探索。但是我经常把最后0的数量写错,所以我只是想知道这样写是否更好:

#define UNEXPLORED 1000000;
color[i] = UNEXPLORED;

我记得在某处我读到过,最好避免使用#define。这样对吗?您将如何解决这个问题?

【问题讨论】:

标签: c++ c-preprocessor preprocessor-directive


【解决方案1】:

对于简单的常量,您可以使用const 或新的constexpr

constexpr unsigned int UNEXPLORED = 1000000;

在这种情况下,使用constconstexpr 没有区别。但是,标记为 constexpr 的“变量”在编译时而不是在运行时进行评估,并且可以在仅接受文字的地方使用。

【讨论】:

  • 在这里使用auto 有什么特别的原因吗?我看到的auto 的主要用途是避免重复类型名称或拼写复杂的嵌套类型。这两者都不是,并且在此处明确说明类型可以提高清晰度。 (同样,为什么是constexpr,而不是传统的const?)
  • @JamesKanze - 在这里使用auto 将产生一个足够大以容纳实际值的整数类型。 unsigned int 不一定大到足以容纳 1000000,这是宏不存在的问题。
  • @PeteBecker 这是一个有趣的方面,我没有想到。根据上下文,它可能被视为劣势或优势。 (我假设如果1000000 不适合unsigned int,编译器至少会发出警告。)
  • @JamesKanze - 我知道我在打死马,但在大多数情况下,宏工作正常,不需要巴洛克式的变通方法。 <g>
  • @PeteBecker 正如您所指出的,宏的作用与auto 相同。 (几乎:如果一个内联函数将它的push_back 放入一个向量中,#define 是唯一没有未定义行为的解决方案。或者枚举常量,但这只有在你有很多的情况下才是合理的其中,你想分组,或者你想限制范围,比如在一个类中。)使用显式类型可确保常量具有该类型。有时可能会有用。
【解决方案2】:

例如使用常量。

const unsigned int UNEXPLORED = 1000000;

或枚举

enum { UNEXPLORED = 1000000 };

【讨论】:

  • 这让我越来越困惑。 :( 我正在尝试学习一个特定的部分,然后又来了成千上万的其他部分。:( ENUM 和 STRUCT 有什么区别?哪个用于什么?
  • @gen 对于这样的问题,我建议你抓住good book 来指导你的学习。
  • 第一个不适用于unsigned int 太小而无法容纳1000000 的系统。并且创建一个 enum 只是为了保存一个常量是 icky。 <g>
【解决方案3】:

在使用常量时,上面的两个答案都是正确的,但是#define 不限于单独使用。另一个使用#define 的例子是宏。

宏是预处理器使用的代码片段,它们的工作方式与其他 #define 声明在这方面完全一样。预处理器将用宏的代码从字面上交换您定义的符号的出现。一个例子:

#define HELLO_MAC do{ std::cout << "Hello World" << std::endl; }while(false)

int main(int argc, char** argv)
{
     HELLO_MAC;
}

这实际上会用我声明的代码替换HELLO_MAC 符号。如果它是一个常数,它会做同样的事情。因此,您可以将常量的#defines 视为一种特殊的宏。

您还可以使用宏传递参数,我发现它对于在代码上执行日志记录/异常策略特别有用。 例如

#define THROW_EXCEPT( ex_type, ex_msg ) /
    do{ throw ex_type( buildExString( (ex_msg), __LINE__, __FILE__ ) ); }while(false)

... 
// somewhere else
THROW_EXCEPT( std::runtime_error, "Unsupported operation in current state" );

该代码允许我确保每个人都使用引发异常的文件行进行记录。

模板通常是比宏更好的选择,但我不能在这个示例中使用模板函数,因为我需要从 throw 的位置使用 __LINE____FILE__ 函数,而不是从模板的位置功能。

哪些地方不应该使用宏?你可以在任何地方使用其他东西。像任何#define 一样,宏都经过预处理,因此编译器根本看不到它们。这意味着永远不会为HELLO_MACTHROW_EXCEPT 创建任何符号,因此在调试器中看不到它们。如果您遇到编译错误,它们也可能会令人困惑,尤其是当它们是长宏时。

【讨论】:

    猜你喜欢
    • 2011-07-10
    • 1970-01-01
    • 1970-01-01
    • 2011-03-10
    • 2018-06-16
    • 2011-03-26
    • 2019-08-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多