如果要将某个常量值与类关联起来,可以采用以下两种方法来实现相同的目标:
class Foo
{
public:
static const size_t Life = 42;
};
class Bar
{
public:
enum {Life = 42};
};
从客户的角度来看,从语法和语义上看,它们似乎是相同的:
size_t fooLife = Foo::Life;
size_t barLife = Bar::Life;
除了纯粹的风格之外,还有什么理由可以解释为什么一个人会比另一个更受欢迎?
答案 0 :(得分:56)
enum
hack过去是必要的,因为许多编译器不支持值的就地初始化。由于这不再是问题,请选择其他选项。现代编译器也能够优化这个常量,因此不需要存储空间。
不使用static const
变体的唯一原因是,如果您希望禁止获取值的地址:您无法获取enum
的地址你可以获取一个常量的地址值(这会提示编译器毕竟为该值保留空间,但只有如果其地址真的被采用)。
此外,除非明确定义,否则获取地址将产生链接时错误。请注意,它仍然可以在声明站点初始化:
struct foo {
static int const bar = 42; // Declaration, initialization.
};
int const foo::bar; // Definition.
答案 1 :(得分:11)
他们不完全相同:
size_t *pLife1 = &Foo::Life;
size_t *pLife2 = &Bar::Life;
答案 2 :(得分:9)
一个区别是enum定义了一个可以用作方法参数的类型,例如,以获得更好的类型检查。两者都被编译器视为编译时常量,因此它们应该生成相同的代码。
答案 3 :(得分:7)
static const
值被视为r值,就像enum
一样。常量r值永远不会为它们生成内存。优势enum
常数是它们不能成为另外1%的l值。 static const
值是类型安全的,允许使用浮点数,c字符串等
如果Foo::Life
具有与之关联的内存,编译器将使&Foo::Life;
成为l值。通常的方法是采取其地址。例如int foo = rand()? Foo::Life: Foo::Everthing;
以下是GCC将使用地址的一个微妙示例:
Life
编译器生成的代码使用Everything
和Foo::Life
的地址。更糟糕的是,这只会产生关于Foo::Everything
和class Foo {
public:
constexpr size_t Life = 42;
};
缺少地址的链接器错误。这种行为完全符合标准,但显然不合需要。还有其他编译器特定的方法可以实现,并且所有标准符合。
一旦你有一个符合要求的c ++ 11编译器,正确的代码就是
{{1}}
这保证始终是一个l值,它是类型安全的,两全其美。
答案 4 :(得分:5)
好吧,如果需要,你可以获取静态const成员值的地址。你必须声明一个枚举类型的单独成员变量来获取它的地址。
答案 5 :(得分:5)
一个微妙的区别是枚举必须在标题中定义,并且对所有人都可见。当你避免依赖时,这是一个痛苦。例如,在PImpl中,添加枚举有点适得其反:
// MyPImpl.hpp
class MyImpl ;
class MyPimpl
{
public :
enum { Life = 42 } ;
private :
MyImpl * myImpl ;
}
另一个第三个解决方案将是问题中提出的“const static”替代方案的变体:在标头中声明变量,但在源中定义它:
// MyPImpl.hpp
class MyImpl ;
class MyPimpl
{
public :
static const int Life ;
private :
MyImpl * myImpl ;
}
// MyPImpl.cpp
const int MyPImpl::Life = 42 ;
请注意MyPImpl :: Life的用户对MyPImpl(包括MyPImpl.hpp)的用户是隐藏的。
这将使MyPimpl作者能够根据需要更改“Life”的值,而不需要重新编译MyPImpl用户,这也是PImpl的总体目标。
答案 6 :(得分:3)
enum hack值得了解有几个原因。首先,枚举黑客行为在某种程度上更像是#define而不是const,有时这就是你想要的。例如,获取const的地址是合法的,但获取枚举的地址是不合法的,并且获取#define的地址通常也是不合法的。如果你不想让人们获得一个指针或引用你的一个整数常量,枚举是一种强制执行该约束的好方法。 (有关通过编码决策强制执行设计约束的更多信息,请参阅第18项。)另外,虽然好的编译器不会为整数类型的const对象留出存储空间(除非你创建一个指针或对象的引用),但是草率的编译器可以,你可能不愿意为这些对象留出记忆。与#defines一样,枚举永远不会导致那种不必要的内存分配。
Effictive c ++ Book