static const vs #define

Pat*_*ola 200 c c++ const

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

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

Ton*_*roy 233

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

  • 枚举
    • 仅适用于整数值
    • 正确处理范围/标识符冲突问题处理得很好,特别是在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从程序逻辑是"合法的"透视?)
    • enum的typename可能出现在RTTI,编译器消息等各个地方 - 可能有用,可能是混淆
    • 如果没有翻译单元实际看到值,就不能使用枚举,这意味着库API中的枚举需要在头中公开的值,make而其他基于时间戳的重新编译工具将在更改时触发客户端重新编译(糟糕! )
  • consts
    • 正确处理范围/标识符冲突问题处理得很好
    • 强大的,单一的,用户指定的类型
      • 您可能会尝试"键入" #defineala #define S std::string("abc"),但常量避免在每个使用点重复构造不同的临时值
    • 一个定义规则并发症
    • 可以获取地址,创建对它们的const引用等.
    • 最类似于非const值,如果在两者之间切换,则最小化工作和影响
    • value可以放在实现文件中,允许本地化重新编译,只需要客户端链接来获取更改
  • 定义
    • "全局"范围/更容易出现冲突的用法,这会产生难以解决的编译问题和意外的运行时结果,而不是明智的错误消息; 减轻这种情况需要:
      • 长,模糊和/或集中协调的标识符,对它们的访问不能从隐式匹配used/current/Koenig-looking-up命名空间,命名空间别名等中受益.
      • 虽然特朗普最佳实践允许模板参数标识符为单字符大写字母(可能后跟数字),但是其他使用不带小写字母的标识符通常是为预处理器定义保留的,并且是预期的(在OS和C/C++库之外)头).这对于企业级预处理器使用来说是非常重要的,以保持可管理性.第三方图书馆可以遵守.观察这意味着现有的consts或enums迁移到/从定义涉及大写的变化,因此需要编辑客户端源代码而不是"简单"重新编译.(就个人而言,我将枚举的第一个字母大写,但不是有效,所以我也会在这两个之间进行迁移 - 也许是时候重新考虑一下了.)
    • 可能的更多编译时操作:字符串文字串联,字符串化(取其大小),串联到标识符
      • 缺点是给定#define X "x"和一些客户端使用ala "pre" X "post",如果你想或需要使X成为运行时可更改的变量而不是常量,则强制编辑客户端代码(而不仅仅是重新编译),而这种转换更容易从a const char*const std::string给定它们已经迫使用户把串联操作(例如"pre" + X + "post"对于string)
    • 不能sizeof直接在定义的数字文字上使用
    • 无类型(如果比较,GCC不会发出警告unsigned)
    • 某些编译器/链接器/调试器链可能不会显示标识符,因此您将被简化为查看"魔术数字"(字符串,无论......)
    • 不能拿地址
    • 在创建#define的上下文中,替换值不一定是合法的(或离散的),因为它在每个使用点进行评估,因此您可以引用尚未声明的对象,依赖于不需要的"实现"预先包含,创建"常量",如{ 1, 2 }可用于初始化数组,或#define MICROSECONDS *1E-6等等.(绝对不推荐这个!)
    • 一些特殊的东西__FILE__,__LINE__可以并入宏观替代
    • 你可以测试#if语句中存在和值是否有条件地包括代码(比预处理后"if"更强大,因为如果预处理器没有选择代码就不需要编译),使用#undef-ine,重新定义等.
    • 取代的文字必须公开:
      • 在它使用的翻译单元中,这意味着客户端使用的库中的宏必须位于标题中,因此make其他基于时间戳的重新编译工具将在更改时触发客户端重新编译(错误!)
      • 或者在命令行上,需要更加小心以确保重新编译客户端代码(例如,Makefile或提供定义的脚本应列为依赖项)

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

  • @Unknown123:随意发布一个 - 我不介意你从这里窃取任何你认为值得的点。干杯 (2认同)

T.E*_*.D. 133

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

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

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

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

namespace {
   unsigned const seconds_per_minute = 60;
};

int main (int argc; char *argv[]) {
...
}
Run Code Online (Sandbox Code Playgroud)

  • 不使用任何内存的`#define`优点是不准确的.示例中的"60"必须存储在某处,无论它是"static const"还是"#define".事实上,我已经看到使用#define导致大量(只读)内存消耗的编译器,而静态const则没有使用不需要的内存. (57认同)
  • @theReverend字面值是否可以免于消耗机器资源?不,他们可能会以不同的方式使用它们,也许它不会出现在堆栈或堆上,但在某些时候程序会被加载到内存中以及编译到其中的所有值. (26认同)
  • @ gilad-naor,你是正确的,但像60这样的小整数实际上有时可能是一种部分例外.一些指令集能够直接在指令流中编码整数或整数子集.例如,MIP立即添加(http://www.cs.umd.edu/class/sum2003/cmsc311/Notes/Mips/addi.html).在这种情况下,一个#defined整数真的可以说不使用空格,因为在编译后的二进制文件中它占用指令中的一些备用位,无论如何都必须存在. (13认同)
  • *String*常量特别是可能受益于`#define`d的那些,至少如果它们可以用作更大字符串常量的"构建块".请参阅我的回复中的示例. (8认同)
  • #define就像是你输入的那样,所以它绝对不是来自内存. (3认同)

AnT*_*AnT 45

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

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

字符串常量BTW是此类异常的一个示例.使用#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;
Run Code Online (Sandbox Code Playgroud)

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

  • “_根本没有理由选择#define_”而不是什么?头文件中定义的静态变量? (2认同)

Per*_*tio 5

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

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


RED*_*AIR 5

  • static const是有类型的(它有一个类型),并且可以由编译器检查其有效性、重新定义等。
  • #define 可以被重新定义、未定义等等。

通常你应该更喜欢 static consts。它没有任何缺点。预处理器应该主要用于条件编译(有时可能用于非常肮脏的技巧)。


Jun*_*zed 5

#define can lead to unexpected results:

#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;
}
Run Code Online (Sandbox Code Playgroud)

Outputs an incorrect result:

y is 505
z is 510
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

It outputs the correct result:

y is 505
z is 1010
Run Code Online (Sandbox Code Playgroud)

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

  • 这就是为什么 #define 表达式应始终加括号的原因 (3认同)