我们说我有这个Haskell代码:
data RigidBody = RigidBody Vector3 Vector3 Float Shape -- position, velocity, mass and shape
data Shape = Ball Float -- radius
| ConvexPolygon [Triangle]
用C ++表达这个的最好方法是什么?
struct Rigid_body {
glm::vec3 position;
glm::vec3 velocity;
float mass;
*???* shape;
};
我问的问题是如何在结构中表示形状,它可以是两种类型中的一种。
答案 0 :(得分:10)
可以使用不同的方法在C ++中解决该问题。
您将定义接口Shape
的纯OO方法,并将两个不同的选项作为实现该接口的派生类型。然后,RigidBody
将包含指向Shape
的指针,该指针将设置为引用Ball
或ConvexPolygon
。亲:人们喜欢OO(不确定这是专业人员:)),它很容易扩展(你可以在以后添加更多形状而不改变类型)。 Con:你应该为Shape
定义一个合适的接口,它需要动态分配内存。
将OO放在一边,你可以使用boost::variant
或类似的类型,它基本上是一个标记的联合,它将包含其中一种类型。 Pro:没有动态分配,形状是对象的本地。骗局:不是纯粹的OO(人们喜欢OO,你还记得吗?),不那么容易扩展,不能使用一般的形状
答案 1 :(得分:5)
为了在这里抛出另一种可能性,您还可以使用boost::variant
作为std::variant
添加到C ++ 17中的标准库中:
struct Ball { float radius; };
struct ConvexPolygon { Triangle t; }
using Shape = boost::variant<Ball, ConvexPolygon>;
这种方法的优点:
一些缺点:
答案 2 :(得分:2)
在C ++中执行此操作的规范方法是Justin Wood的答案中给出的基于继承的解决方案。通常,您为Shape
赋予了每种Shape
但是,C ++也有union
种类型。您可以改为使用“已标记的工会”:
struct Ball { /* ... */ };
struct Square { /* ... */ };
struct Shape {
int tag;
union {
Ball b;
Square s;
/* ... */
}
};
您使用tag
成员说出Shape
是Ball
还是Square
还是其他任何内容。您可以switch
成员tag
以及
这样做的缺点是,Shape
比int
和Ball
以及其他人的Square
大一{{}}}; OCaml中的对象,以及没有这个问题。
您使用哪种技术取决于您使用Shape
的方式。
答案 3 :(得分:0)
您将要创建基类Shape
。在这里,您可以创建实际的形状类Ball
和ConvexPolygon
。您将要确保Ball
和ConvexPolygon
是基类的子级。
class Shape {
// Whatever commonalities you have between the two shapes, could be none.
};
class Ball: public Shape {
// Whatever you need in your Ball class
};
class ConvexPolygon: public Shape {
// Whatever you need in your ConvexPolygon class
};
现在,您可以制作像这样的通用对象
struct Rigid_body {
glm::vec3 position;
glm::vec3 velocity;
float mass;
Shape *shape;
};
当您实际初始化shape
变量时,可以使用Ball
或ConvexPolygon
类对其进行初始化。您可以继续制作任意数量的形状。