使用static const
vars比使用#define
预处理器更好吗?或者也许取决于背景?
每种方法的优点/缺点是什么?
就个人而言,我讨厌预处理器,所以我总是选择const。
#define的主要优点是它不需要存储在程序中的内存,因为它实际上只是用文字值替换了一些文本。它还具有没有类型的优点,因此它可以用于任何整数值而不会产生警告。
“const”的优点是它们可以作用域,并且可以在需要传递指向对象的指针的情况下使用它们。
我不知道你对“静态”部分到底发生了什么。如果你在全局声明,我会把它放在一个自治命名空间而不是静态。例如
namespace {
unsigned const seconds_per_minute = 60;
};
int main (int argc; char *argv[]) {
...
}
如果要定义要在类的所有实例之间共享的常量,请使用static const。如果常量特定于每个实例,只需使用const(但请注意,该类的所有构造函数都必须在初始化列表中初始化此const成员变量)。
一切的利弊,取决于用法:
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
和其他基于时间戳的重新编译工具将在更改时触发客户端重新编译(错误) !)#define
ala #define S std::string("abc")
,但常数避免在每个使用点重复构造不同的临时
一个定义规则并发症
可以获取地址,创建对它们的const引用等。
最类似于非const
值,如果在两者之间切换,可以最大限度地减少工作和影响
value可以放在实现文件中,允许本地化重新编译,只需要客户端链接来获取更改#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或脚本应列为依赖项)作为一般规则,我使用const
s并将它们视为一般用法的最专业选项(尽管其他人具有吸引这个老懒惰程序员的简单性)。
如果这是一个C ++问题并且它提到#define
作为替代,那么它是关于“全局”(即文件范围)常量,而不是关于类成员。当谈到C ++中的这些常量时,static const
是多余的。在C ++中,const
默认有内部链接,并且没有必要声明它们static
。所以它真的是关于const
与#define
。
最后,在C ++中,const
更受欢迎。至少因为这些常量是键入和作用域的。除了极少数例外,没有理由更喜欢#define
而不是const
。
字符串常量BTW是此类异常的一个示例。使用#define
d字符串常量,可以使用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 ++。我想知道这个问题是否被正确标记了......
使用静态const就像在代码中使用任何其他const变量一样。这意味着您可以在信息来自的任何地方进行跟踪,而不是在预编译过程中仅在代码中替换的#define。
您可能需要查看C ++ FAQ Lite来解决这个问题:http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.7
通常你应该更喜欢静态的。它没有缺点。 prprocessor应主要用于条件编译(有时可能用于非常脏的trics)。
#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
只是替换了文本。因为这样做会严重影响操作顺序,所以我建议使用常量变量。
通常一个const声明(注意它不需要是静态的)是要走的路
不建议使用预处理器指令#define
定义常量,不仅适用于C++
,还适用于C
。这些常量不具有该类型。甚至在C
被提议使用const
作为常数。
总是喜欢使用语言功能而不是预处理器等其他工具。
ES.31:不要将宏用于常量或“函数”
宏是bug的主要来源。宏不遵守通常的范围和类型规则。宏不遵守通常的参数传递规则。宏确保人类读者看到与编译器看到的不同的东西。宏使工具构建复杂化。