如何在派生类上强制执行静态成员?

wsa*_*eem 6 c++ methods virtual static

我有一个基类,Primitive从中派生我其他几个classes-- Sphere,Plane

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

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

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

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

Luc*_*ore 6

让我们考虑一下。我确定您不仅只有2个子容器,所以让我们对此进行概括。

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

如果要添加更多的类,则应在尽可能最少的位置更改代码。

因为intersect操作是可交换的,相交的代码AB应在相同的位置相交的代码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);
};
Run Code Online (Sandbox Code Playgroud)

我们已经决定不希望在Plane或中添加交集逻辑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&);
};
Run Code Online (Sandbox Code Playgroud)

这是您将在其中添加新功能和新逻辑的类。例如,如果您决定添加一个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);
    }
};
Run Code Online (Sandbox Code Playgroud)

在我们的原始方法中,我们需要:

struct Sphere : Primitive
{
    virtual void intersect(Primitive* other)
    {
        SphereIntersectBehavior intersectBehavior;
        return intersectBehavior.doIntersect(other);
    }
};
Run Code Online (Sandbox Code Playgroud)

更加干净的设计将是实施工厂,以抽象出行为的实际类型:

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

而且您甚至不需要intersect虚拟,因为它只会对每个班级都这样做。

如果您遵循这种设计

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

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


jua*_*nza 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";

}
Run Code Online (Sandbox Code Playgroud)

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

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