考虑到我有一些既有枚举又有类行为的“东西”。举个例子,考虑一个具有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 ++。
答案 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_happy
,rotate
和intensity
的定义拆分为他们自己的'facet'类,这是真正的C ++精神。
答案 2 :(得分:2)
两个让步:如果你想要成员函数(似乎
合理的)那么它必须是一个阶级;和不同的东西
仅在属性(例如Red
,Green
和Blue
)中不
有不同的类型。
目前尚不清楚为什么要将颜色数限制为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
,它有三个子类Red
,Green
和Blue
,并且具有静态常量RED
,{{1分别和GREEN
:
BLUE
这实际上类似于用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
作为参数的函数