静态常量与#define

使用static constvariables比#define预处理器更好吗? 或者也许这取决于上下文?

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

就个人而言,我讨厌预处理器,所以我会一直跟着const。

#define的主要优点是它不需要内存来存储你的程序,因为它实际上只是用一个文字值replace一些文本。 它也有没有types的优点,所以它可以用于任何整数值而不会产生警告。

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

虽然我不清楚你在“静态”部分得到了什么。 如果你在全局声明,我会把它放在一个独立的命名空间,而不是使用静态。 例如

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

优点和缺点,取决于使用情况:

  • 枚举
    • 只适用于整数值
    • 正确的范围/标识符冲突问题处理得很好,特别是在C ++ 11枚举类中枚举类枚举enum class X消除的范围X::
    • 强types,但是对于一个足够大的带符号或无符号的int大小,在C ++ 03中你无法控制(虽然你可以指定一个位域,如果枚举是struct / class / union),而C ++ 11默认为int但可以由程序员明确设置
    • 不能采取地址 – 没有一个因为枚举值在使用点被有效地内联取代
    • 更强大的使用限制(例如递增 – template <typename T> void f(T t) { cout << ++t; }将不会编译,尽pipe您可以将枚举包装到具有隐式构造函数的类中,定义运营商)
    • 每个常量的types从封闭的枚举中取出,所以template <typename T> void f(T)在从不同的枚举中传递相同的数值时会得到不同的实例化,所有这些都与实际的f(int)实例不同。 每个函数的目标代码可能是相同的(忽略地址偏移量),但我不希望编译器/链接器消除不必要的副本,尽pipe如果你在意你可以检查你的编译器/链接器。
    • 即使使用typeof / decltype,也不能期望numeric_limits提供对有意义的值和组合的有用的了解(事实上,在源代码中甚至没有“合法的”组合,考虑enum { A = 1, B = 2 } – 从程序逻辑的angular度来看, A|B “合法的”)
    • 枚举的types名可能出现在RTTI,编译器消息等各种地方 – 可能是有用的,可能是混淆
    • 如果没有翻译单元实际上看到这个值,就不能使用枚举,这意味着库API中的枚举需要在头文件中公开的值,并且make和其他基于时间戳的重新编译工具在更改时会触发客户端重新编译(坏的!)
  • consts
    • 正确的范围/标识冲突问题很好地处理
    • 强大的,单一的,用户指定的types
      • 你可以尝试“键入”一个#define ala #define S std::string("abc") ,但是这个常量避免了在每个使用点重复build立不同的临时对象
    • 一个定义规则并发症
    • 可以接收地址,创build对它们的const引用等。
    • 与非常量值非常相似,如果在两者之间进行切换,则最大限度地减less了工作和影响
    • 值可以放置在实现文件中,允许本地化的重新编译,只是客户端链接来接受更改
  • 定义
    • “全局”范围/更容易出现冲突的用法,这会产生难以解决的编译问题和意外的运行时结果,而不是理智的错误消息; 减轻这一点需要:
      • 冗长,模糊和/或集中协调的标识符,并且访问它们不能从隐式地匹配使用过的/当前的/ Koenig查找的名称空间,命名空间别名等中受益。
      • 而最佳实践允许模板参数标识符是单字符大写字母(可能后面跟着一个数字),其他使用不带小写字母的标识符通常是预处理器定义的预期和预期的(在OS和C / C ++库之外头)。 这对于企业规模的预处理器使用情况保持可pipe理性非常重要。 第三方库可望被遵守。 观察到这一点意味着现有常量或枚举从定义迁移到定义涉及到大写的变化,因此需要编辑客户端源代码而不是“简单的”重新编译。 (就我个人而言,我把第一个枚举的字母大写,但不是常数,所以我会在这两个之间移动 – 也许是时候重新考虑了。
    • 更多的编译时操作可能:string字面连接,string化(取其大小),连接成标识符
      • 缺点是给定#define X "x"和一些客户端使用ala "pre" X "post" ,如果你想要或者需要使X成为一个运行时可变的variables而不是一个常量,你强制编辑客户端代码(而不仅仅是重新编译),而那个过渡更容易从const char*const std::string给定,因为它们已经强制用户并入级联操作(例如"pre" + X + "post" for string
    • 不能直接在定义的数字文字上使用sizeof
    • 无types(如果与unsigned比较,GCC不会发出警告)
    • 一些编译器/链接器/debugging器链可能不会提供标识符,所以你会减less看“魔术数字”(string,无论…)
    • 不能拿地址
    • 在创build#define的上下文中,replace值不需要是合法的(或离散的),因为它在每个使用点都被评估过,所以你可以引用尚未声明的对象,取决于不需要的“实现”预先包含,创build可用于初始化数组的{ 1, 2 }常量,或者#define MICROSECONDS *1E-6等( 绝对不build议这样做!)。
    • 一些特殊的东西,如__FILE____LINE__可以被纳入macros替代
    • 你可以在#if语句中testing是否有条件地包含代码(比后处理“if”更强大,因为如果没有被预处理器select,代码不需要编译),使用#undef -ine,重新定义等。
    • 替代文本必须被暴露:
      • 在翻译单元中使用它,这意味着客户端使用的库中的macros必须位于头文件中,所以make和其他基于时间戳的重新编译工具在更改时会触发客户端重新编译(不好!)
      • 或者在命令行中,需要更多的关注以确保客户端代码被重新编译(例如,提供定义的Makefile或脚本应该被列为依赖项)

作为一般规则,我使用const s并将它们视为一般用法中最专业的选项(尽pipe其他人对这个懒惰的程序员有着简单的吸引力)。

如果这是一个C ++的问题,它提到#define作为替代,那么它是关于“全局”(即文件范围)常量,而不是关于类成员。 当谈到在C + +这样的常量static const是多余的。 在C ++中, const默认具有内部链接,将它们声明为static是没有意义的。 所以它实际上是关于const#define

最后,在C ++中const是更可取的。 至less因为这样的常量是键入和范围的。 除了less数例外之外,没有理由select#define不是const

string常量,BTW,是这种例外的一个例子。 使用#define dstring常量,可以使用C / C ++编译器的编译时间连接function

 #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; 

PS再次强调,当有人提到static const作为#define的替代时,通常意味着他们正在谈论C而不是C ++。 我不知道这个问题是否正确标记…

使用静态常量就像在代码中使用其他常量variables一样。 这意味着您可以跟踪信息的来源,而不是在预编译过程中代码中简单replace的#define。

你可能想看看这个问题的C ++ FAQ Lite: http : //www.parashift.com/c++-faq-lite/newbie.html#faq-29.7

  • 一个静态常量是键入的(它有一个types),可以由编译器检查有效性,重定义等。
  • #define可以被重定义为undefined。

通常你应该更喜欢静态常量。 它没有缺点。 预处理器应该主要用于条件编译(有时也可能是非常脏的trics)。

请看这里: 静态常量vs定义

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

使用预处理器指令#define定义常量不build议不仅适用于C++ ,也适用于C 这些常量不会有types。 即使在C被build议使用const为常量。

如果你正在定义一个在类的所有实例之间共享的常量,使用static const。 如果这个常量是特定于每个实例的,那么就使用const(但是请注意,类的所有构造函数都必须在初始化列表中初始化这个const成员variables)。

总是倾向于使用语言function,而不是像预处理器那样的其他工具。

ES.31:不要使用macros作常量或“function”

macros是错误的主要来源。 macros不遵守通常的范围和types规则。 macros不遵守通常的parameter passing规则。 macros确保读者看到与编译器所看到的不同的东西。 macros使工具构build复杂化。

从C ++核心指南