首页 文章

static const vs #define

提问于
浏览
187

使用 static const vars比 #define 预处理器更好吗?或者也许取决于背景?

每种方法的优点/缺点是什么?

10 回答

  • 217

    就个人而言,我讨厌预处理器,所以我总是选择const .

    #define的主要优点是它不需要存储在程序中的内存,因为它实际上只是用文字值替换了一些文本 . 它还具有没有类型的优点,因此它可以用于任何整数值而不会产生警告 .

    “const”的优点是它们可以作用域,并且可以在需要传递指向对象的指针的情况下使用它们 .

    我不知道你对“静态”部分到底发生了什么 . 如果你在全局声明,我会把它放在一个自治命名空间而不是静态 . 例如

    namespace {
       unsigned const seconds_per_minute = 60;
    };
    
    int main (int argc; char *argv[]) {
    ...
    }
    
  • 0

    一切的利弊,取决于用法:

    • 枚举

    • 仅适用于整数值

    • 正确处理范围/标识符冲突问题处理得很好,特别是在C11枚举类中, enum class X 的枚举由范围 X:: 消除歧义

    • 强类型,但是对于一个足够大的有符号或无符号的int大小,你在C 03中没有控制权(尽管你可以指定一个位域,如果枚举是struct / class的成员,它们应该被打包到哪个位/ union),而C 11默认为 int ,但可以由程序员明确设置

    • 可以't take the address - there isn' t,因为枚举值在使用点有效地替换为内联

    • 更强的使用限制(例如递增 - template <typename T> void f(T t) { cout << ++t; } 将无法编译,但您可以将枚举包装到具有隐式构造函数的类中,转换运算符和用户定义的运算符)

    • 每个常量的类型取自封闭的枚举,因此 template <typename T> void f(T) 从不同的枚举中传递相同的数值时获得不同的实例化,所有这些都与任何实际的 f(int) 实例不同 . 每个函数's object code could be identical (ignoring address offsets), but I wouldn' t都希望编译器/链接器能够消除不必要的副本,但是如果您愿意,可以检查编译器/链接器 .

    • 即使使用typeof / decltype,也可以't expect numeric_limits to provide useful insight into the set of meaningful values and combinations (indeed, 3010121 combinations aren'甚至在源代码中注明,从程序逻辑的角度考虑 enum { A = 1, B = 2 } - 是 A|B "legal"?)

    • 枚举的typename可能出现在RTTI,编译器消息等各个地方 - 可能有用,可能是混淆

    • 如果没有翻译单元实际看到该值,则无法使用枚举,这意味着库API中的枚举需要标头中公开的值,而 make 和其他基于时间戳的重新编译工具将在更改时触发客户端重新编译(坏!)

    • consts

    • 正确处理了作用域/标识符冲突问题

    • 强大,单一,用户指定的类型

    • 你可能会尝试"type" a #define ala #define S std::string("abc") ,但常数避免在每个使用点重复构造不同的临时

    • 一个定义规则并发症

    • 可以获取地址,创建对它们的const引用等 .

    • 最类似于非 const 值,如果在两者之间切换,可以最大限度地减少工作和影响

    • 值可以放在实现文件中,允许本地化重新编译,只需客户端链接即可获取更改

    • 定义

    • "global"范围/更容易出现冲突的使用,这会产生难以解决的编译问题和意外的运行时结果,而不是明智的错误消息;减轻这种情况需要:

    • 长,模糊和/或集中协调的标识符,并且对它们的访问不能从隐式匹配used / current / Koenig-looking-up命名空间,命名空间别名等中受益 .

    • 虽然特朗普最佳实践允许模板参数标识符为单字符大写字母(可能后跟一个数字),但其他使用不带小写字母的标识符通常是为预处理器定义保留的,并且是预期的(在OS和C / C之外)库 Headers ) . 这对于企业级预处理器使用来说是非常重要的,以保持可管理性 . 第三方图书馆可以遵守 . 观察这意味着现有的consts或enums迁移到/从定义涉及大写的变化,因此需要编辑客户端源代码而不是"simple"重新编译 . (就个人而言,我将枚举的第一个字母大写,但不是有效,所以我会在这两者之间进行迁移 - 也许是时候重新考虑一下了 . )

    • 更多可能的编译时操作:字符串文字串联,字符串化(取其大小),串联到标识符

    • 缺点是给定 #define X "x" 和一些客户端使用ala "pre" X "post" ,如果你想或需要使X成为运行时可更改的变量而不是常量,则强制编辑客户端代码(而不仅仅是重新编译),而这种转换更容易从 const char*const std::string ,因为他们已经强制用户合并连接操作(例如 "pre" + X + "post" for string

    • 不能直接在已定义的数字文字上使用 sizeof

    • 无类型(如果与 unsigned 相比,GCC不会发出警告)

    • 某些编译器/链接器/调试器链可能不会显示标识符,因此您将被缩减为查看"magic numbers"(字符串,等等......)

    • 不能取地址

    • 在创建#define的上下文中,替换值不一定是合法的(或离散的),因为它预先包含在内,创建"constants",例如可用于初始化数组的 { 1, 2 } ,或 #define MICROSECONDS *1E-6 等 . 不推荐这个!)

    • 一些特殊的东西,如 __FILE____LINE__ 可以合并到宏替换中

    • 您可以在 #if 语句中测试存在和值是否有条件地包括代码(比预处理后更强大"if",因为如果预处理器未选择代码则无需编译),使用 #undef -ine,重新定义等 .
      必须公开

    • 替换文字:

    • 在它所使用的翻译单元中,这意味着用于客户端的库中的宏必须位于 Headers 中,因此 make 和其他基于时间戳的重新编译工具将在它们被更改时触发客户端重新编译(坏!)

    • 或在命令行上,需要更加小心以确保重新编译客户端代码(例如,提供定义的Makefile或脚本应列为依赖项)

    作为一般规则,我使用 const 并认为它们是一般用法的最专业选项(尽管其他一些对这个老懒惰的程序员有吸引力) .

  • 2

    如果这是一个C问题并且它提到 #define 作为替代,那么它是关于"global"(即文件范围)常量,而不是关于类成员 . 当谈到C static const 中的这些常数是多余的 . 在C _3010157中,默认情况下有内部链接,声明它们没有意义 static . 所以它真的是关于 const#define .

    最后,在C _3010161中最好 . 至少因为这些常量是键入和作用域的 . 除少数例外情况外,没有理由更喜欢 #define 而不是 const .

    字符串常量BTW是此类异常的一个示例 . 使用 #define d字符串常量,可以使用C / C编译器的编译时串联功能,如

    #define OUT_NAME "output"
    #define LOG_EXT ".log"
    #define TEXT_EXT ".txt"
    
    const char *const log_file_name = OUT_NAME LOG_EXT;
    const char *const text_file_name = OUT_NAME TEXT_EXT;
    

    附:再次,以防万一有人提到 static const 作为 #define 的替代,通常意味着他们在谈论C,而不是C . 我想知道这个问题是否被正确标记了......

  • 42

    使用静态const就像在代码中使用任何其他const变量一样 . 这意味着您可以跟踪信息来自何处,而不是在预编译过程中将简单地替换为代码的#define .

    您可能需要查看C FAQ Lite以获取此问题:http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.7

  • 5
    • 输入一个静态const(它有一个类型),编译器可以检查它是否有效,重新定义等 .

    • #define可以重新定义为undefined .

    通常你应该更喜欢静态的 . 它没有缺点 . prprocessor应主要用于条件编译(有时可能用于非常脏的trics) .

  • 127

    请看这里:static const vs define

    通常一个const声明(注意它不需要是静态的)是要走的路

  • 1

    不建议使用预处理程序指令 #define 定义常量,不仅适用于 C++ ,还适用于 C . 这些常量不具有该类型 . 即使在 C 被提议使用 const 作为常量 .

  • 0

    总是喜欢使用语言功能而不是预处理器等其他工具 .

    ES.31:不要将宏用于常量或“函数”宏是错误的主要来源 . 宏不遵守惯例范围和类型规则 . 宏不遵守通常的参数传递规则 . 宏确保人类读者看到与编译器看到的不同的东西 . 宏使工具构建复杂化 .

    C++ Core Guidelines

  • 4

    如果要定义要在类的所有实例之间共享的常量,请使用static const . 如果常量特定于每个实例,只需使用const(但请注意,该类的所有构造函数都必须在初始化列表中初始化此const成员变量) .

  • 2

    #define 会导致意外结果:

    #include <iostream>
    
    #define x 500
    #define y x + 5
    
    int z = y * 2;
    
    int main()
    {
        std::cout << "y is " << y;
        std::cout << "\nz is " << z;
    }
    

    输出不正确的结果:

    y is 505
    z is 510
    

    但是,如果用常量替换它:

    #include <iostream>
    
    const int x = 500;
    const int y = x + 5;
    
    int z = y * 2;
    
    int main()
    {
        std::cout << "y is " << y;
        std::cout << "\nz is " << z;
    }
    

    它输出正确的结果:

    y is 505
    z is 1010
    

    这是因为 #define 只是替换了文本 . 因为这样做会严重影响操作顺序,所以我建议使用常量变量 .

相关问题