静态常量成员值与成员枚举:哪种方法更好,为什么?
如果你想把一些常数值和一个类关联起来,这里有两种方法可以达到同样的目的:
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 const
variables的唯一原因是,如果你想禁止取值的地址:你不能取一个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;
编译器生成的代码使用Life
和Everything
的地址。 更糟糕的是,这只会产生一个关于Foo::Life
和Foo::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 + +书