“静态常量”与“#define”

2024-02-15

是不是比较好用static const变量比#define预处理器?或者这可能取决于上下文?

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


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

  1. enums:

    • 仅适用于整数值
    • 适当的范围/标识符冲突问题得到了很好的处理,特别是在C++11 https://en.wikipedia.org/wiki/C%2B%2B11枚举类,其中枚举为enum class X通过范围消除歧义X::
    • 强类型,但是足够大的有符号或无符号 int 大小,您无法控制C++03 https://en.wikipedia.org/wiki/C%2B%2B03(尽管如果枚举是 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 } - is A|B从程序逻辑的角度来看“合法”?)
    • 枚举的类型名可能出现在不同的地方RTTI https://en.wikipedia.org/wiki/Run-time_type_information、编译器消息等 - 可能有用,可能混淆
    • 如果翻译单元没有实际看到该值,则无法使用枚举,这意味着库 API 中的枚举需要标头中公开的值,并且make和其他基于时间戳的重新编译工具将在更改时触发客户端重新编译(糟糕!)

  1. consts:

    • 范围适当/标识符冲突问题得到很好的处理
    • strong, single, user-specified type
      • 你可以尝试“输入”a#define ala #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:
      • 长的、晦涩的和/或集中协调的标识符,并且对它们的访问不能从隐式匹配使用的/当前的/Koenig查找的命名空间、命名空间别名等中受益。
      • 虽然压倒性的最佳实践允许模板参数标识符为单字符大写字母(可能后跟数字),但不带小写字母的标识符的其他使用通常保留并期望预处理器定义(在操作系统和 C/C++ 库之外)标题)。这对于企业规模的预处理器使用保持可管理性非常重要。第三方库预计会遵守。观察到这一点意味着现有常量或枚举与定义的迁移涉及大小写的更改,因此需要对客户端源代码进行编辑,而不是“简单”的重新编译。 (就我个人而言,我将枚举的第一个字母大写,但不将常量大写,所以我也会在这两个字母之间迁移 - 也许是时候重新考虑这一点了。)
    • more compile-time operations possible: string literal concatenation, stringification (taking size thereof), concatenation into identifiers
      • 缺点是给定#define X "x"和一些客户使用ala"pre" X "post",如果您想要或需要使 X 成为运行时可更改的变量而不是常量,您可以强制编辑客户端代码(而不仅仅是重新编译),而从const char* or const std::string鉴于它们已经强制用户合并串联操作(例如"pre" + X + "post" for string)
    • 不能使用sizeof直接在定义的数字文字上
    • 无类型(如果与unsigned)
    • 一些编译器/链接器/调试器链可能不显示标识符,因此您将不得不查看“幻数”(字符串,无论如何......)
    • 无法获取地址
    • 在创建 #define 的上下文中,替换值不需要是合法的(或离散的),因为它在每个使用点进行评估,因此您可以引用尚未声明的对象,这取决于不需要的“实现”预先包含,创建“常量”,例如{ 1, 2 }可用于初始化数组,或者#define MICROSECONDS *1E-6 etc. (确实不推荐这个!)
    • 一些特别的东西,比如__FILE__ and __LINE__可以合并到宏替换中
    • 您可以测试其存在和价值#if有条件地包含代码的语句(比后预处理“if”更强大,因为如果预处理器未选择,则代码不需要编译),请使用#undef-ine、重新定义等
    • substituted text has to be exposed:
      • 在它所使用的翻译单元中,这意味着供客户端使用的库中的宏必须位于标头中,因此make和其他基于时间戳的重新编译工具将在更改时触发客户端重新编译(糟糕!)
      • 或者在命令行上,需要更加小心以确保重新编译客户端代码(例如,提供定义的 Makefile 或脚本应列为依赖项)

我个人的看法:

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

“静态常量”与“#define” 的相关文章

随机推荐