C++ 静态常量 vs #define

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1637332/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:41:13  来源:igfitidea点击:

static const vs #define

c++cconst

提问by Patrice Bernassola

Is it better to use static constvars than #definepreprocessor? Or maybe it depends on the context?

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

What are advantages/disadvantages for each method?

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

采纳答案by T.E.D.

Personally, I loathe the preprocessor, so I'd always go with const.

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

The main advantage to a #defineis that it requires no memory to store in your program, as it is really just replacing some text with a literal value. It also has the advantage that it has no type, so it can be used for any integer value without generating warnings.

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

Advantages of "const"s are that they can be scoped, and they can be used in situations where a pointer to an object needs to be passed.

" const"s 的优点是它们可以限定作用域,它们可以用于需要传递指向对象的指针的情况。

I don't know exactly what you are getting at with the "static" part though. If you are declaring globally, I'd put it in an anonymous namespace instead of using static. For example

不过,我不知道您对 " static" 部分的确切含义。如果您是全局声明,我会将它放在匿名命名空间中,而不是使用static. 例如

namespace {
   unsigned const seconds_per_minute = 60;
};

int main (int argc; char *argv[]) {
...
}

回答by Tony Delroy

Pros and cons between #defines, consts and (what you have forgot) enums, depending on usage:

#defines, consts 和 (你忘记了什么) enums之间的优缺点,取决于用法:

  1. enums:

    • only possible for integer values
    • properly scoped / identifier clash issues handled nicely, particularly in C++11 enum classes where the enumerations for enum class Xare disambiguated by the scope X::
    • strongly typed, but to a big-enough signed-or-unsigned int size over which you have no control in C++03 (though you can specify a bit field into which they should be packed if the enum is a member of struct/class/union), while C++11 defaults to intbut can be explicitly set by the programmer
    • can't take the address - there isn't one as the enumeration values are effectively substituted inline at the points of usage
    • stronger usage restraints (e.g. incrementing - template <typename T> void f(T t) { cout << ++t; }won't compile, though you can wrap an enum into a class with implicit constructor, casting operator and user-defined operators)
    • each constant's type taken from the enclosing enum, so template <typename T> void f(T)get a distinct instantiation when passed the same numeric value from different enums, all of which are distinct from any actual f(int)instantiation. Each function's object code could be identical (ignoring address offsets), but I wouldn't expect a compiler/linker to eliminate the unnecessary copies, though you could check your compiler/linker if you care.
    • even with typeof/decltype, can't expect numeric_limits to provide useful insight into the set of meaningful values and combinations (indeed, "legal" combinations aren't even notated in the source code, consider enum { A = 1, B = 2 }- is A|B"legal" from a program logic perspective?)
    • the enum's typename may appear in various places in RTTI, compiler messages etc. - possibly useful, possibly obfuscation
    • you can't use an enumeration without the translation unit actually seeing the value, which means enums in library APIs need the values exposed in the header, and makeand other timestamp-based recompilation tools will trigger client recompilation when they're changed (bad!)
  1. enums:

    • 仅适用于整数值
    • 适当的范围/标识符冲突问题得到了很好的处理,特别是在 C++11 枚举类中,其中的枚举enum class X被范围消除了歧义X::
    • 强类型,但要足够大的有符号或无符号 int 大小,您在 C++03 中无法控制(尽管如果枚举是 struct/ class/union),而 C++11 默认为int但可以由程序员显式设置
    • 无法获取地址 - 没有地址,因为枚举值在使用点被有效地内联替换
    • 更强的使用限制(例如递增 -template <typename T> void f(T t) { cout << ++t; }不会编译,但您可以将枚举包装到具有隐式构造函数、强制转换运算符和用户定义运算符的类中)
    • 每个常量的类型取自封闭的枚举,因此template <typename T> void f(T)当从不同的枚举传递相同的数值时获得不同的实例化,所有这些都与任何实际f(int)实例化不同。每个函数的目标代码可能是相同的(忽略地址偏移),但我不希望编译器/链接器消除不必要的副本,尽管您可以检查编译器/链接器(如果您关心)。
    • 即使使用 typeof/decltype,也不能指望 numeric_limits 能够提供对一组有意义的值和组合的有用洞察(实际上,源代码中甚至没有标记“合法”组合,考虑enum { A = 1, B = 2 }-A|B从程序逻辑中是“合法的”看法?)
    • 枚举的类型名可能出现在 RTTI、编译器消息等的不同位置 - 可能有用,可能是混淆
    • 在翻译单元没有实际看到值的情况下,您不能使用枚举,这意味着库 API 中的枚举需要在标头中公开的值,make并且其他基于时间戳的重新编译工具将在更改时触发客户端重新编译(糟糕! )


  1. consts:

    • properly scoped / identifier clash issues handled nicely
    • strong, single, user-specified type
      • you might try to "type" a #defineala #define S std::string("abc"), but the constant avoids repeated construction of distinct temporaries at each point of use
    • One Definition Rule complications
    • can take address, create const references to them etc.
    • most similar to a non-constvalue, which minimises work and impact if switching between the two
    • value can be placed inside the implementation file, allowing a localised recompile and just client links to pick up the change
  1. consts:

    • 范围适当/标识符冲突问题处理得很好
    • 强、单一、用户指定的类型
      • 您可能会尝试“键入” a #defineala #define S std::string("abc"),但常量可避免在每个使用点重复构建不同的临时变量
    • 一个定义规则的复杂性
    • 可以获取地址,创建对它们的常量引用等。
    • 最类似于非const值,如果在两者之间切换,它可以最大限度地减少工作和影响
    • 值可以放置在实现文件中,允许本地化重新编译和客户端链接来获取更改


  1. #defines:

    • "global" scope / more prone to conflicting usages, which can produce hard-to-resolve compilation issues and unexpected run-time results rather than sane error messages; mitigating this requires:
      • long, obscure and/or centrally coordinated identifiers, and access to them can't benefit from implicitly matching used/current/Koenig-looked-up namespace, namespace aliases etc.
      • while the trumping best-practice allows template parameter identifiers to be single-character uppercase letters (possibly followed by a number), other use of identifiers without lowercase letters is conventionally reserved for and expected of preprocessor defines (outside the OS and C/C++ library headers). This is important for enterprise scale preprocessor usage to remain manageable. 3rd party libraries can be expected to comply. Observing this implies migration of existing consts or enums to/from defines involves a change in capitalisation, and hence requires edits to client source code rather than a "simple" recompile. (Personally, I capitalise the first letter of enumerations but not consts, so I'd be hit migrating between those two too - maybe time to rethink that.)
    • more compile-time operations possible: string literal concatenation, stringification (taking size thereof), concatenation into identifiers
      • downside is that given #define X "x"and some client usage ala "pre" X "post", if you want or need to make X a runtime-changeable variable rather than a constant you force edits to client code (rather than just recompilation), whereas that transition is easier from a const char*or const std::stringgiven they already force the user to incorporate concatenation operations (e.g. "pre" + X + "post"for string)
    • can't use sizeofdirectly on a defined numeric literal
    • untyped (GCC doesn't warn if compared to unsigned)
    • some compiler/linker/debugger chains may not present the identifier, so you'll be reduced to looking at "magic numbers" (strings, whatever...)
    • can't take the address
    • the substituted value need not be legal (or discrete) in the context where the #define is created, as it's evaluated at each point of use, so you can reference not-yet-declared objects, depend on "implementation" that needn't be pre-included, create "constants" such as { 1, 2 }that can be used to initialise arrays, or #define MICROSECONDS *1E-6etc. (definitelynot recommending this!)
    • some special things like __FILE__and __LINE__can be incorporated into the macro substitution
    • you can test for existence and value in #ifstatements for conditionally including code (more powerful than a post-preprocessing "if" as the code need not be compilable if not selected by the preprocessor), use #undef-ine, redefine etc.
    • substituted text has to be exposed:
      • in the translation unit it's used by, which means macros in libraries for client use must be in the header, so makeand other timestamp-based recompilation tools will trigger client recompilation when they're changed (bad!)
      • or on the command line, where even more care is needed to make sure client code is recompiled (e.g. the Makefile or script supplying the definition should be listed as a dependency)
  1. #defines:

    • “全局”范围/更容易出现冲突用法,这会产生难以解决的编译问题和意外的运行时结果,而不是正常的错误消息;缓解这种情况需要:
      • 长的、晦涩的和/或集中协调的标识符,并且对它们的访问不能从隐式匹配 used/current/Koenig-looked-up 命名空间、命名空间别名等中受益。
      • 虽然胜过的最佳实践允许模板参数标识符是单字符大写字母(可能后跟一个数字),但其他不带小写字母的标识符的使用通常是为预处理器定义保留和预期的(在 OS 和 C/C++ 库之外)头)。这对于企业级预处理器的使用保持可管理性很重要。预计第 3 方图书馆将遵守。观察到这意味着将现有的常量或枚举迁移到/从定义涉及大小写的变化,因此需要对客户端源代码进行编辑,而不是“简单”的重新编译。(就我个人而言,我将枚举的第一个字母大写,而不是常量,所以我也会在这两者之间迁移 - 也许是时候重新考虑一下了。)
    • 更多可能的编译时操作:字符串文字连接、字符串化(取其大小)、连接成标识符
      • 缺点是给定的#define X "x"和某些客户端使用 ala "pre" X "post",如果您希望或需要使 X 成为运行时可更改的变量而不是常量,您可以强制编辑客户端代码(而不仅仅是重新编译),而从 aconst char*const std::string给定它们的转换更容易已经强制用户合并连接操作(例如"pre" + X + "post"for string
    • 不能sizeof直接在定义的数字文字上使用
    • 无类型(如果与 相比,GCC 不会发出警告unsigned
    • 某些编译器/链接器/调试器链可能不提供标识符,因此您将只能查看“幻数”(字符串,无论如何……)
    • 拿不到地址
    • 在创建#define 的上下文中,替换值不需要是合法的(或离散的),因为它在每个使用点都被评估,因此您可以引用尚未声明的对象,取决于不需要的“实现”预先包含,创建“常量”,例如{ 1, 2 }可用于初始化数组#define MICROSECONDS *1E-6等(绝对不推荐!)
    • 一些特殊的东西,比如__FILE____LINE__可以合并到宏替换中
    • 您可以在#if有条件地包含代码的语句中测试存在和值(比后预处理“if”更强大,因为如果预处理器未选择代码,则无需编译),使用#undef-ine,重新定义等。
    • 必须公开替换文本:
      • 在它所使用的翻译单元中,这意味着客户端使用的库中的宏必须在标头中,因此make其他基于时间戳的重新编译工具将在更改时触发客户端重新编译(糟糕!)
      • 或在命令行上,需要更加小心以确保重新编译客户端代码(例如,应将提供定义的 Makefile 或脚本列为依赖项)


My personal opinion:

我的个人意见:

As a general rule, I use consts and consider them the most professional option for general usage (though the others have a simplicity appealing to this old lazy programmer).

作为一般规则,我使用consts 并认为它们是一般用途中最专业的选项(尽管其他的简单性吸引了这个老懒惰的程序员)。

回答by AnT

If this is a C++ question and it mentions #defineas an alternative, then it is about "global" (i.e. file-scope) constants, not about class members. When it comes to such constants in C++ static constis redundant. In C++ consthave internal linkage by default and there's no point in declaring them static. So it is really about constvs. #define.

如果这是一个 C++ 问题并且它#define作为替代提到,那么它是关于“全局”(即文件范围)常量,而不是关于类成员。当涉及到 C++ 中的此类常量时,它static const是多余的。在 C++ 中const,默认情况下有内部链接,声明它们没有意义static。所以这真的是关于constvs. #define.

And, finally, in C++ constis preferable. At least because such constants are typed and scoped. There are simply no reasons to prefer #defineover const, aside from few exceptions.

最后,在 C++ 中const更可取。至少因为这样的常量是类型化和作用域的。有根本没有理由喜欢#defineconst从少数例外,一边。

String constants, BTW, are one example of such an exception. With #defined string constants one can use compile-time concatenation feature of C/C++ compilers, as in

字符串常量,顺便说一句,就是这种异常的一个例子。使用#defined 字符串常量可以使用 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;

P.S. Again, just in case, when someone mentions static constas an alternative to #define, it usually means that they are talking about C, not about C++. I wonder whether this question is tagged properly...

PS 同样,以防万一,当有人提到static const作为 的替代品时#define,通常意味着他们在谈论 C,而不是 C++。我想知道这个问题是否被正确标记......

回答by Juniorized

#definecan lead to unexpected results:

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

Outputs an incorrect result:

输出错误的结果:

y is 505
z is 510

However, if you replace this with constants:

但是,如果您将其替换为常量:

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

It outputs the correct result:

它输出正确的结果:

y is 505
z is 1010

This is because #definesimply replaces the text. Because doing this can seriously mess up order of operations, I would recommend using a constant variable instead.

这是因为#define简单地替换了文本。因为这样做会严重扰乱操作顺序,所以我建议改用常量变量。

回答by Percutio

Using a static const is like using any other const variables in your code. This means you can trace wherever the information comes from, as opposed to a #define that will simply be replaced in the code in the pre-compilation process.

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

You might want to take a look at the C++ FAQ Lite for this question: http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.7

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

回答by RED SOFT ADAIR

  • A static const is typed (it has a type) and can be checked by the compiler for validity, redefinition etc.
  • a #define can be redifined undefined whatever.
  • 静态常量是类型化的(它有一个类型),并且可以由编译器检查其有效性、重新定义等。
  • #define 可以重新定义为 undefined 。

Usually you should prefer static consts. It has no disadvantage. The prprocessor should mainly be used for conditional compilation (and sometimes for really dirty trics maybe).

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

回答by RED SOFT ADAIR

Defining constants by using preprocessor directive #defineis not recommended to apply not only in C++, but also in C. These constants will not have the type. Even in Cwas proposed to use constfor constants.

#define不建议使用预处理器指令定义常量不仅适用于C++,而且适用于C。这些常量不会有类型。即使在C被提议const用于常量。

回答by Hitokage

Always prefer to use the language features over some additional tools like preprocessor.

总是更喜欢使用语言特性而不是一些额外的工具,比如预处理器。

ES.31: Don't use macros for constants or "functions"

Macros are a major source of bugs. Macros don't obey the usual scope and type rules. Macros don't obey the usual rules for argument passing. Macros ensure that the human reader sees something different from what the compiler sees. Macros complicate tool building.

ES.31:不要对常量或“函数”使用宏

宏是错误的主要来源。宏不遵守通常的范围和类型规则。宏不遵守参数传递的通常规则。宏确保人类读者看到的东西与编译器看到的不同。宏使工具构建复杂化。

From C++ Core Guidelines

来自C++ 核心指南

回答by ennuikiller

Please see here: static const vs define

请参见此处: 静态常量与定义

usually a const declaration (notice it doesn't need to be static) is the way to go

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

回答by snr

If you are defining a constant to be shared among all the instances of the class, use static const. If the constant is specific to each instance, just use const (but note that all constructors of the class must initialize this const member variable in the initialization list).

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