static const vs #define

问题描述 投票:187回答:10

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

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

c++ c const
10个回答
130
投票

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

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

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

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

namespace {
   unsigned const seconds_per_minute = 60;
};

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

0
投票

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


223
投票

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

  • 枚举 仅适用于整数值 正确处理范围/标识符冲突问题,特别是在C ++ 11枚举类中,其中enum class X的枚举由范围X::消除歧义 强类型,但是对于一个足够大的有符号或无符号的int大小,在C ++ 03中你无法控制它(尽管你可以指定一个位域,如果enum是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 正确处理范围/标识符冲突问题处理得很好 强大的,单一的,用户指定的类型 你可能会尝试“键入”#define ala #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成为运行时可更改的变量而不是常量,则强制编辑客户端代码(而不仅仅是重新编译),而这种转换更容易从const char*或者const std::string,因为他们已经强迫用户合并连接操作(例如"pre" + X + "post" for string) 不能直接在定义的数字文字上使用sizeof 无类型(如果与unsigned相比,海湾合作委员会不会发出警告) 某些编译器/链接器/调试器链可能不会显示标识符,因此您将被简化为查看“魔术数字”(字符串,无论......) 不能拿地址 在创建#define的上下文中,替换值不一定是合法的(或离散的),因为它在每个使用点进行评估,因此您可以引用尚未声明的对象,依赖于不需要的“实现”预先包含,创建“常量”,如{ 1, 2 },可用于初始化数组,或#define MICROSECONDS *1E-6等(绝对不推荐这个!) 一些特殊的东西,如__FILE____LINE__可以纳入宏观替代 您可以在#if语句中测试存在和值是否有条件地包括代码(比预处理后“if”更强大,因为如果预处理器没有选择代码就不需要编译),使用#undef-ine,重新定义等。 取代的文字必须公开: 在它使用的翻译单元中,这意味着客户端使用的库中的宏必须位于标题中,因此make和其他基于时间戳的重新编译工具将在它们被更改时触发客户端重新编译(不好!) 或者在命令行上,需要更加小心以确保重新编译客户端代码(例如,提供定义的Makefile或脚本应列为依赖项)

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


42
投票

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

最后,在C ++中,const更受欢迎。至少因为这些常量是键入和作用域的。除了极少数例外,没有理由更喜欢#define而不是const

字符串常量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;

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


5
投票

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

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


4
投票
  • 输入一个静态const(它有一个类型),编译器可以检查它是否有效,重新定义等。
  • #define可以重新定义为undefined。

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


3
投票

#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只是替换了文本。因为这样做会严重影响操作顺序,所以我建议使用常量变量。


2
投票

请看这里:static const vs define

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


2
投票

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


1
投票

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

ES.31:不要将宏用于常量或“函数”

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

来自C++ Core Guidelines

© www.soinside.com 2019 - 2024. All rights reserved.