在类中声明枚举

174
在下面的代码片段中,Color 枚举类型在 Car 类内声明,目的是为了限制该枚举类型的作用域,尽量不影响全局命名空间。
class Car
{
public:

   enum Color
   {
      RED,
      BLUE,
      WHITE
   };

   void SetColor( Car::Color color )
   {
      _color = color;
   }

   Car::Color GetColor() const
   {
      return _color;
   }

private:

   Car::Color _color;

};

(1) 这是限制 Color 枚举范围的好方法吗?还是应该将其声明在 Car 类之外,但可能在自己的命名空间或结构体内?我今天刚看到了这篇文章,它提倡后者并讨论了一些关于枚举的好处:http://gamesfromwithin.com/stupid-c-tricks-2-better-enums

(2) 在这个例子中,当在类内部工作时,最好将枚举编码为 Car::Color,还是只使用 Color 就足够了?(我认为前者更好,以防全局命名空间中声明了另一个 Color 枚举。这样,我们至少可以明确地指定我们所引用的枚举。)

5个回答

101
  1. 如果Color只与Car相关,那么您可以限制其范围。如果其他类也使用了另一个Color枚举,那么最好将其设置为全局(或者至少在Car之外)。

  2. 没有区别。如果存在全局变量,则仍然会使用本地变量,因为它更接近当前范围。请注意,如果您将这些函数定义在类定义之外,则需要在函数的接口中明确指定Car::Color


12
是的和不是。Car::Color getColor() 返回一个颜色,但 void Car::setColor(Color c) 不返回任何东西,因为在 setColor 中已经有指定了。 - Matthieu M.

95

现在 - 使用C++11 - 你可以使用 枚举类 来实现此功能:

enum class Color { RED, BLUE, WHITE };

据我所知,这正是你想要的。


4
遗憾的是,它不允许成员函数:https://dev59.com/EGEi5IYBdhLWcg3wZ7y8#53284026 - Andreas detests censorship

77
我更喜欢以下方法(下面是代码)。 它解决了“命名空间污染”的问题,而且更加类型安全(你不能将两个不同的枚举互相赋值或比较,也不能将你的枚举与其他任何内置类型进行比较等)。
struct Color
{
    enum Type
    {
        Red, Green, Black
    };
    Type t_;
    Color(Type t) : t_(t) {}
    operator Type () const {return t_;}
private:
   //prevent automatic conversion for any other built-in types such as bool, int, etc
   template<typename T>
    operator T () const;
};

使用方法:

Color c = Color::Red;
switch(c)
{
   case Color::Red:
     //некоторый код
   break;
}
Color2 c2 = Color2::Green;
c2 = c; //error
c2 = 3; //error
if (c2 == Color::Red ) {} //error
If (c2) {} error

我创建宏来方便使用:

#define DEFINE_SIMPLE_ENUM(EnumName, seq) \
struct EnumName {\
   enum type \
   { \
      BOOST_PP_SEQ_FOR_EACH_I(DEFINE_SIMPLE_ENUM_VAL, EnumName, seq)\
   }; \
   type v; \
   EnumName(type v) : v(v) {} \
   operator type() const {return v;} \
private: \
    template<typename T> \
    operator T () const;};\

#define DEFINE_SIMPLE_ENUM_VAL(r, data, i, record) \
    BOOST_PP_TUPLE_ELEM(2, 0, record) = BOOST_PP_TUPLE_ELEM(2, 1, record),

使用方法:

DEFINE_SIMPLE_ENUM(Color,
             ((Red, 1))
             ((Green, 3))
             )

一些参考资料:

  1. Herb Sutter, Jum Hyslop, C/C++用户杂志, 22(5), 2004年5月
  2. Herb Sutter, David E. Miller, Bjarne Stroustrup 强类型枚举(第3版),2007年7月

我喜欢这个。它还强制枚举使用有效值进行实例化。我认为赋值运算符和复制构造函数会很有用。此外,t_ 应该是私有的。我可以不用宏。 - jmucchiello
我也喜欢这个。谢谢提供参考资料。 - anio
1
你说:“而且它更加类型安全(你不能分配甚至比较两个不同的枚举...” 为什么你认为这是一个好的特性?我认为 if(c2 == Color::Red ) 是合理的,必须编译通过,但在你的例子中却没有。对于赋值也是同样的论点! - Nawaz
3
@c2 是另一种类型(Color2),所以你为什么认为 c2 == Color::Red 和赋值应该能编译通过呢?如果 Color::Red 等于1,而 Color2::Red 等于2 呢?Color::Red == Color2::Red 应该评估为 true 还是 false 呢?如果混合使用不安全的枚举类型,那么将会带来麻烦。 - Victor K
@VictorK:哦,我没有注意到c2是不同类型的!现在这篇文章对我来说有意义了。谢谢。(+1) - Nawaz
2
为什么Type t_;不是private的? - Zingam

7

通常,我会将我的枚举放在一个 struct 结构体中。我看到过一些指南,包括“前缀”。

enum Color
{
  Clr_Red,
  Clr_Yellow,
  Clr_Blue,
};

我一直认为这看起来更像是 C 的指导方针而不是 C++ 的(因为缩写和 C++ 中的命名空间)。

因此,为了限制范围,我们现在有两个选择:

  • 命名空间
  • 结构体/类

我个人倾向于使用 struct,因为它可以用作模板编程的参数,而命名空间无法被操作。

操纵的示例包括:

template <class T>
size_t number() { /**/ }

这段代码返回结构体 T 中枚举类型元素的数量 :)


3
如果您正在创建代码库,那么我建议使用命名空间。但是,在该命名空间中仍然只能有一个Color枚举。如果您需要一个可能使用常见名称但可能对不同类使用不同常量的枚举,请使用您的方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接