实现具有枚举和类行为的东西

时间:2014-09-05 12:35:02

标签: c++ class c++11 enums

考虑到我有一些既有枚举又有类行为的“东西”。举个例子,考虑一个具有Color概念的世界,正好有3个:红色,绿色和蓝色。

基于这种颜色,我们也有功能,例如我们可以有一个功能告诉我们颜色是否是一个快乐的颜色,以及其他一些功能:

isHappy: Color -> {yes, no}
intensity: Color -> value
rotate: Color -> Color

要完成类似haskel的语法,我们可以这样做:

data Color = Red | Green | Blue

并实现上述功能。但那就是haskell,它不是C ++,也不像c ++那样没有OO的概念。继续用C ++:

事实上,我们有3种颜色,而不是更多,建议使用枚举;允许我们在源代码中的任何地方使用红色,蓝色和绿色等常量。但是,我们无法在枚举中添加方法,因此isHappy强度和旋转将实现为函数(而不是方法)。

事实上我们有这些方法,其中第一个参数是Color,建议使用一个类。但是,我们可以实例化我们想要的许多类,特别是超过3.这意味着表示Red的两个变量将分配在内存中的不同位置。这有点奇怪,因为Red会有非常“常量”的行为,因为它是不可变的,只能创建三种不同类型的Color对象。此外,我们不能使用红色,绿色和蓝色等符号,但需要将它们存储在变量中。使用全局变量将非常难看。

我们也可以使用继承,其中Red,Green和Blue继承自Color类。这使我们可以非常轻松地微调功能,因为我们可以在我们想要的任何类中实现我们想要的功能。但是,在c ++中具有继承性的OO应用切片。例如,创建包含(红色,绿色或蓝色)列表的向量将非常棘手。或者创建一个存储3种颜色之一的变量:

Color c1 = Red();
Color c2 = Blue();

变量c1和c2将被分配一个不同的对象,但是没有办法真正区分它们。这使得实现operator ==棘手:

class Red : Color { //...
  bool operator==(Color &c) const{
     // no way to determine whether c is Red, Green or Blue.
  }
}

这种情况是否有有效的模式或解决方案?我觉得它看起来很多,因此我很好奇。 C ++ 14-only解决方案也非常受欢迎!

编辑:很多人似乎评论说我提到的解决方案的问题并不是真正的问题。这是一个有效的观点。但是,我不是在寻找可能的解决方案,我正在寻找一种符合c ++(11 | 14)设计原则的解决方案。我也可以使用:

#define RED 0
#define GREEN 1
#define BLUE 2

这样可以完美地运行,但它不是一个好的设计原则,因为它可能会与其他功能相冲突,使用RED,BLUE或GREEN等名称。这在语义上也很奇怪,因为我可以说RED

总结一下,在答案中我希望看到一个坚持良好的c ++设计原则的解决方案。我不关心那些正常工作的解决方案!这可能是前面提到的三种方法之一,使用枚举,一个类或继承。

EDIT2:同时我也提到了基于模板的多继承案例方法。但是,说实话,我对模板的了解还不足以创造出“好”的东西。这个想法基于type_traits标头的std :: is_same和std :: is_functional之类的函数。

#include <iostream>

class Color {};

class Red : Color {};
class Green : Color {};
class Blue : Color {};

template<class C1, class C2>
bool is_same_color();

template<class C1>
bool is_happy();

int main() {
    // your code goes here
    return 0;
}

它不起作用,但我希望这个想法得以实现。另外,我意识到is_same_color和is_happy应该是定义了operator()的类。

EDIT3:有人可以说这就是我想要的:

enum Color {
  RED, GREEN, BLUE
  bool isHappy() { return this==GREEN || this==RED; }
  Color rotate() { return (this==RED ? GREEN (this==GREEN ? BLUE : RED)); }
  int intensity() { return (this==RED ? 11 (this==GREEN ? 12 : 4)); }
}

但这当然不是有效的c ++。

5 个答案:

答案 0 :(得分:2)

您可以使用一个类并为enum使用特定实例(一个单例):

class Color
{
public:
    bool isHappy() const { return this == &Green || this == &Red; }
    const Color* rotate() const { return (this == &Red ? &Green : (this == &Green ? &Blue : &Red)); }
    int intensity() const {return mIntensity; }

    static const Color* red() { return &Red; }
    static const Color* green() { return &Green; }
    static const Color* blue() { return &Blue; }

private:
    // forbid to construct new instance: use only red(), green(), blue()
    explicit Color(int intensity) : mIntensity(intensity) {}
    Color(const Color&) = delete; 
private:
    int mIntensity;

private:
    static const Color Red;
    static const Color Green;
    static const Color Blue;
};

const Color Color::Red(11);
const Color Color::Green(12);
const Color Color::Blue(4);

答案 1 :(得分:2)

已经给出了一些其他好的答案,但我担心他们不会完全使用C ++表达式,清晰度和简洁性。

我提出以下解决方案:

class Color
{
public:
    virtual bool is_happy() = 0;
    virtual Color* rotate() = 0;
    virtual int intensity() = 0;

    static Color* const Red;
    static Color* const Green;
    static Color* const Blue;

    Color(Color const&) = delete;

private:
    Color(){}

    template<bool happiness, Color* const* rotation_result, int intensity_value>
    class Color_Generator;

    template<bool happiness, Color* const* rotation_result, int intensity_value>
    friend class Color_Generator;
};

template<bool happiness, Color* const* rotation_result, int intensity_value>
class Color::Color_Generator : public Color
{
public:
    bool is_happy()
    {
        return happiness;
    }

    Color* rotate()
    {
        return *rotation_result;
    }

    int intensity()
    {
        return intensity_value;
    }

    static Color_Generator<happiness, rotation_result, intensity_value> Instance;
};

template<bool happiness, Color* const* rotation_result, int intensity_value>
Color::Color_Generator<happiness, rotation_result, intensity_value>
Color::Color_Generator<happiness, rotation_result, intensity_value>::Instance;

Color* const Color::Red = &Color_Generator<true, &Green, 11>::Instance;
Color* const Color::Green = &Color_Generator<true, &Blue, 12>::Instance;
Color* const Color::Blue = &Color_Generator<false, &Red, 4>::Instance;

//==============
// Some usage follows

#include <iostream>

int main()
{
    Color* a = Color::Red;
    Color* b = Color::Green;
    Color* c = Color::Blue;

    std::cout << a->intensity() << std::endl;
    std::cout << b->is_happy() << std::endl;
    std::cout << (b->rotate() == c) << std::endl;
}

还有一些C ++功能可用于进一步改进此代码。例如,您可以使用虚拟继承将is_happyrotateintensity的定义拆分为他们自己的'facet'类,这是真正的C ++精神。

答案 2 :(得分:2)

两个让步:如果你想要成员函数(似乎 合理的)那么它必须是一个阶级;和不同的东西 仅在属性(例如RedGreenBlue)中 有不同的类型。

目前尚不清楚为什么要将颜色数限制为3, 但最终,这归结为确保你拥有 正好是你班级的3个实例,而不是更多。最简单的 这样做的方法是使构造函数为private,并使其成为 实例静态成员:

class Color
{
    Color( /* whatever parameters are needed */ );
public:
    static Color red;
    static Color green;
    static Color blue;
    //  ...
};

然后,用户将使用Color :: red,Color :: green和Color :: blue (它有额外的优势,你可以做一些事情 与情绪相似,Mood :: blue不会引起命名 冲突)。

答案 3 :(得分:1)

更新:简化代码

您可以将其实现为基类Color,它有三个子类RedGreenBlue,并且具有静态常量RED,{{1分别和GREEN

BLUE

Here is the demo

这实际上类似于用Java实现枚举的方式。

答案 4 :(得分:0)

如果你想要的只是一个enum和作用于enum的函数,以及一种让世界知道这些函数和这个enum属于一起的方法,那么也许是namespace正是您的工具。

namespace Color
{
enum EColor // or in C++11 use an enum class and set the underlying type to char or such
{
    RED,
    GREEN,
    BLUE
};
bool IsHappy(const EColor & color);
int Intensity(const EColor & color);
EColor Rotate(const EColor & color);
}

你可以很容易地把它变成一个带有成员函数的类,因为每个函数都需要EColor来处理,但是使用命名空间可能会让你更容易将功能拆分成单独的模块,如果不是EColor的每个用户都关心这种颜色的快乐,或者碰巧有一堆与EColor相关但不需要一个EColor作为参数的函数