6

我有一个基类 ,Primitive从中派生出其他几个类—— Sphere,Plane等。

Primitiveintersect()通过纯虚函数对其子类强制执行某些功能,例如。的计算intersect依赖于实例数据,因此将其作为成员方法是有意义的。

我的问题出现在以下方面:我希望每个派生实例都能够识别其类型,比如通过std::string type()成员方法。type()由于同一类的所有实例都将返回相同的类型,因此创建一个方法是有意义的static。因为我也希望每个Primitive子类都实现这个方法,所以我也想让它成为一个纯虚函数,就像intersect()上面一样。

但是,C++ 中不允许使用静态虚拟方法。 C++ 静态虚拟成员?我们可以有一个虚拟静态方法吗(c++) 提出类似的问题,但它们不包括在派生类上强制执行函数的要求。

任何人都可以帮我解决上述问题吗?

4

4 回答 4

6

让我们考虑一下。我确定您不仅有 2 个子类,所以让我们概括一下。

首先想到的是代码重复、可扩展性和紧密性。让我们扩展这些:

如果您想添加更多类,您应该在尽可能少的地方更改代码。

因为intersect操作是可交换的,相交A和的代码B应该和相交的代码在同一个地方BA所以将逻辑保留在类本身是不可能的。

此外,添加一个新类并不意味着您必须修改现有的类,而是扩展一个委托类(是的,我们将在这里讨论模式)。

这是您当前的结构,我假设(或类似的,可能是 的返回类型intersect,但现在并不重要):

struct Primitive
{
    virtual void intersect(Primitive* other) = 0;
};
struct Sphere : Primitive
{
    virtual void intersect(Primitive* other)
};
struct Plane : Primitive
{
    virtual void intersect(Primitive* other);
};

我们已经决定不希望在Planeor中使用交集逻辑Sphere,因此我们创建了一个新的class

struct Intersect
{
    static void intersect(const Sphere&, const Plane&);
    //this again with the parameters inversed, which just takes this
    static void intersect(const Sphere&, const Sphere&);
    static void intersect(const Plane&, const Plane&);
};

这是您将添加新功能和新逻辑的类。例如,如果您决定添加一个Line类,您只需添加方法intersec(const Line&,...)

请记住,在添加新类时,我们不想更改现有代码。所以我们无法检查相交函数中的类型。

我们可以为此创建一个行为类(策略模式),它的行为取决于类型,然后我们可以扩展:

struct IntersectBehavior
{  
    Primitive* object;
    virtual void doIntersect(Primitive* other) = 0;
};
struct SphereIntersectBehavior : IntersectBehavior
{
    virtual void doIntersect(Primitive* other)
    {
        //we already know object is a Sphere
        Sphere& obj1 = (Sphere&)*object;
        if ( dynamic_cast<Sphere*>(other) )
            return Intersect::intersect(obj1, (Sphere&) *other);
        if ( dynamic_cast<Plane*>(other) )
            return Intersect::intersect(obj1, (Plane&) *other);

        //finally, if no conditions were met, call intersect on other
        return other->intersect(object);
    }
};

在我们原来的方法中,我们有:

struct Sphere : Primitive
{
    virtual void intersect(Primitive* other)
    {
        SphereIntersectBehavior intersectBehavior;
        return intersectBehavior.doIntersect(other);
    }
};

一个更简洁的设计是实现一个工厂,以抽象出行为的实际类型:

struct Sphere : Primitive
{
    virtual void intersect(Primitive* other)
    {
        IntersectBehavior*  intersectBehavior = BehaviorFactory::getBehavior(this);
        return intersectBehavior.doIntersect(other);
    }
};

你甚至不需要intersect是虚拟的,因为它会为每个班级做这个。

如果你按照这个设计

  • 添加新类时无需修改现有代码
  • 将实现放在一个地方
  • IntersectBehavior仅为每种新类型扩展
  • Intersect类中为新类型提供实现

我敢打赌,这可以进一步完善。

于 2012-05-08T16:53:36.970 回答
2

由于他们在您提供的链接中讨论的原因,您不能将虚拟成员设为静态。

您关于在派生类上强制执行函数的要求的问题是通过在抽象基类中使函数纯虚拟来处理的,这将强制派生类必须实现该函数。

于 2012-05-08T16:05:00.800 回答
1

由于同一类的所有实例都将返回相同的类型,因此将 type() 设为静态方法是有意义的。

不,不是的。当您不需要对象的实例来调用函数时,您可以使用静态方法。在这种情况下,您正在尝试识别对象的类型,因此您确实需要一个实例。

无论如何,所有方法体都由所有对象共享,因此无需担心重复。一个例外是函数是内联的,但编译器会尽力减少开销,如果成本太大,可能会将其变为非内联。

PS 要求一个类在类层次结构之外标识自己通常是一种不好的代码气味。尝试寻找另一种方式。

于 2012-05-08T17:15:07.327 回答
0

您可以有一个非静态虚拟方法调用静态方法(或返回静态字符串),在每个派生类中适当地实现。

#include <iostream>
#include <string>

struct IShape {
  virtual const std::string& type() const =0;
};

struct Square : virtual public IShape {
  virtual const std::string& type() const { return type_; }
  static std::string type_;
};
std::string Square::type_("square");

int main() {

  IShape* shape = new Square;
  std::cout << shape->type() << "\n";

}

请注意,type()无论如何您都必须为每个子类实现该方法,因此您能做的最好的事情就是使字符串保持静态。但是,您可以考虑使用 anenum代替字符串,避免在代码中进行不必要的字符串比较。

现在,回到问题的根本,我认为设计有些缺陷。您不能真正拥有适用于各种形状的通用相交函数,因为由相交产生的形状类型差异很大,即使对于相同类型的形状(两个平面可以在一个平面、一条线相交或不相交于全部,例如)。因此,在尝试提供通用解决方案时,您会发现自己到处都在执行这些类型的检查,并且随着您添加的形状越多,这种检查就会变得难以维护。

于 2012-05-08T16:02:33.667 回答