实现同时具有枚举和类行为的东西
Implement something that has both enum and class behavior
考虑一下我有一些同时具有枚举和类行为的"thing"。举个例子,考虑一个有颜色概念的世界,正好有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将具有非常"恒定"的行为,因为它是不可变的,并且只能创建三种不同类型的Color对象。此外,我们不能使用红色、绿色和蓝色等符号,但需要将它们存储在变量中。使用全局变量将是非常丑陋的imho。
我们也可以使用继承,其中红色、绿色和蓝色继承自Color类。这使我们能够非常容易地微调功能,因为我们可以在任何我们想要的类中实现我们想要的东西。然而,在c++中具有继承性的OO应用了切片。例如,创建一个包含(红色、绿色或蓝色)列表的向量将是非常棘手的。或者创建一个存储3种颜色之一的变量:
Color c1 = Red();
Color c2 = Blue();
变量c1和c2将被分配一个不同的对象,但没有办法真正区分它们。这使得实现操作符==变得棘手:
class Red : Color { //...
bool operator==(Color &c) const{
// no way to determine whether c is Red, Green or Blue.
}
}
对于这种情况,是否有有效的模式或解决方案?我觉得它经常出现,所以我很好奇。仅C++14的解决方案也非常感谢!
编辑:许多人似乎认为我提到的解决方案的问题并不是真正的问题。这是一个有效的观点。然而,我并不是在寻找一个可能的解决方案,我正在寻找一个符合良好的c++(11|14)设计原则的解决方案。我也可以使用:
#define RED 0
#define GREEN 1
#define BLUE 2
这将非常好,但这不是一个好的设计原则,因为它可能会与其他功能发生冲突,使用红色、蓝色或绿色等名称。这在语义上也很奇怪,因为我可以说RED
总之,在答案中,我希望看到一个坚持良好的c++设计原则的解决方案。我不在乎那些有效的解决方案!这可能是前面提到的三种方法之一,使用枚举、一个类或继承。
EDIT2:同时,我还想到了一种基于模板的方法来处理多重继承情况。然而,老实说,我对模板的了解还不足以创造出"好"的东西。这个想法是基于type_traits头的std::is_same和std::is _function等函数。
#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应该是定义了运算符()的类。
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++。
您可以使用一个类并为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);
已经给出了一些其他不错的答案,但恐怕他们没有充分使用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++特性可用于进一步改进此代码。例如,您可以使用虚拟继承,以便按照C++的真正精神,将is_happy
、rotate
和intensity
的定义拆分为它们自己的"facet"类。
两个给定:如果您想要成员函数(看起来合理),则它必须是一个类;以及不同的东西只有在属性(如Red
、Green
和Blue
)中才应该有不同的类型。
目前还不清楚为什么要将颜色数量限制为3,但最终,这归结为确保你的类正好有3个实例,不再有了。最简单的这样做的方法是使构造函数私有实例静态成员:
class Color
{
Color( /* whatever parameters are needed */ );
public:
static Color red;
static Color green;
static Color blue;
// ...
};
然后,用户将使用颜色:红色、颜色:绿色和颜色:蓝色(这还有一个额外的优势,那就是你可以做点什么与moods类似,Mood::blue不会引起命名冲突)。
更新:简化代码一点
您可以将其实现为基类Color
,它有三个子类Red
、Green
和Blue
,并分别具有静态常数RED
、GREEN
和BLUE
:
class Red;
class Green;
class Blue;
class Color;
typedef const Color* Color_t;
class Color {
friend class Red;
friend class Green;
friend class Blue;
public:
static Color_t RED;
static Color_t GREEN;
static Color_t BLUE;
virtual std::string name() const = 0;
private:
// prohibit instantiation of non-friend subclasses
virtual ~Color() = default;
static const Red RED_;
static const Green GREEN_;
static const Blue BLUE_;
};
class Red : public Color {
friend class Color;
private:
Red() {}; // prohibit instantiation other than by Color
std::string name() const {return "Red";}
};
class Green : public Color {
friend class Color;
private:
Green() {};
std::string name() const {return "Green";}
};
class Blue : public Color {
friend class Color;
private:
Blue() {};
std::string name() const {return "Blue";}
};
const Red Color::RED_;
const Green Color::GREEN_;
const Blue Color::BLUE_;
Color_t Color::RED = &RED_;
Color_t Color::GREEN = &GREEN_;
Color_t Color::BLUE = &BLUE_;
int main() {
Color_t c = Color::GREEN;
c = Color::BLUE;
if (c == Color::GREEN) {
std::cout << c-> name() << " is green" << std::endl;
} else {
std::cout << c-> name() << " is not green" << std::endl;
}
// we can make collections of colors easily
std::vector<Color_t> colors = { Color::RED, Color::RED, Color::GREEN, Color::BLUE, Color::GREEN };
return 0;
}
这是的演示
这实际上类似于枚举在Java中的实现方式。
如果你想要的只是一个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
作为参数。
- 不带大括号的枚举形式
- 枚举环境变量的惯用C++14/C++17方法
- 类似枚举的计算常量
- 如何正确实现和访问运算符的各种自定义枚举器
- 错误:从"int"到枚举c++的转换无效
- 具有特定于枚举的实现和共享功能 (CRTP) 的类模板
- 如何在V8中为C 本机对象实现属性枚举器
- 如何在C 中实现真正的枚举类
- 强类型枚举的语法实现错误
- 枚举在内部作为整数实现.那为什么会给出错误
- 如何实现像Qt::LeftDockWidgetArea这样的枚举名称
- 在C++中实现枚举类型
- 如何在C++中实现枚举
- 实现同时具有枚举和类行为的东西
- 如何实现枚举值到类模板的隐式转换
- 不确定如何在头文件中实现可访问的枚举
- 枚举是实现位标志的规范方式吗?
- c++枚举的类实现
- 如何使用嵌套枚举实现代码重用
- 枚举成员类型仍然依赖于实现