静态常量成员值与成员枚举:哪种方法更好,为什么?

如果你想把一些常数值和一个类关联起来,这里有两种方法可以达到同样的目的:

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; 

除了纯粹的风格之外,除了别人之外,还有其他的原因吗?

enum hack过去是必要的,因为许多编译器不支持就地初始化值。 由于这不再是一个问题,去其他的select。 现代编译器也能够优化这个常量,所以不需要存储空间。

不使用static constvariables的唯一原因是,如果你想禁止取值的地址:你不能取一个enum值的地址,而你可以取一个常量的地址(这会提示编译器为毕竟价值保留空间,但只有它的地址是真正采取)。

此外,地址的获取将产生链接时间错误,除非常数被明确定义 。 注意,它仍然可以在声明的位置初始化:

 struct foo { static int const bar = 42; // Declaration, initialization. }; int const foo::bar; // Definition. 

他们不一样:

 size_t *pLife1 = &Foo::Life; size_t *pLife2 = &Bar::Life; 

一个区别是枚举定义了一个可以用作方法参数的types,例如,为了得到更好的types检查。 两者都被编译器视为编译时间常量,所以它们应该生成相同的代码。

那么,如果需要,你可以采取一个静态const成员值的地址。 你必须声明一个单独的枚举types的成员variables来获取它的地址。

另一个解决scheme?

一个微妙的区别是,枚举必须在头部定义,并为所有可见。 当你避免依赖,这是一个痛苦。 例如,在PImpl中,添加一个枚举有些适得其反。

 // MyPImpl.hpp class MyImpl ; class MyPimpl { public : enum { Life = 42 } ; private : MyImpl * myImpl ; } 

另一个解决scheme是在这个问题中提出的“常量静态”替代scheme:在头文件中声明variables,但是在源文件中定义variables:

 // 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的总体目标也是如此。

static const量值被视为r值,就像在99%的代码中看到的那样。 常量r值永远不会为它们生成内存。 enum常量的优点是不能在另外的1%中变成l值。 static const量值是types安全的,并允许浮点数,Cstring等。

编译器会将Foo::Life作为一个l值,如果它有内存相关的话。 通常的做法是采取地址。 例如&Foo::Life;

这是一个微妙的例子,GCC将使用这个地址:

 int foo = rand()? Foo::Life: Foo::Everthing; 

编译器生成的代码使用LifeEverything的地址。 更糟糕的是,这只会产生一个关于Foo::LifeFoo::Everything丢失地址的链接器错误。 这种行为完全符合标准,但明显不合要求。 还有其他编译器特定的方式可能会发生,并且所有标准符合。

一旦你有一个符合c + + 11编译器正确的代码将是

 class Foo { public: constexpr size_t Life = 42; }; 

这是保证始终是一个值,它是types安全的,两全其美。

enum hack值得了解,原因有几个。 首先,enum hack在某些方面更像是一个#define,而不是const,有时这就是你想要的。 例如,把一个const的地址作为合法的,但是拿一个枚举的地址是不合法的,通常也是不合法的,也就是#define的地址。 如果你不想让人们得到一个指针或引用你的一个整型常量,那么枚举是强制执行这个约束的一个好方法。 另外,尽pipe好的编译器不会为整型types的const对象留出存储空间(除非你创build了一个指向对象的指针或引用),但是草率的编译器可能,你可能不愿意为这样的对象留下记忆。 像#defines一样,枚举不会导致这种不必要的内存分配。

有效的c + +书