如何强制执行派生类的静态成员?(How to enforce a static member on

2019-06-24 06:47发布

我有一个基类, Primitive ,从中我推导几个其它classes-- SpherePlane

Primitive强制一些功能性,例如intersect()通过纯虚函数它的子类。 的计算intersect取决于实例数据,因此是有意义的具有它作为一个部件的方法。

我的问题出现在以下情况:我希望每一个派生的实例能够识别其类型,通过说std::string type()成员方法。 作为同一类的所有实例都将返还相同种类,它是有道理的,使type()一个static方法。 正如我也希望每一个Primitive子类实现这个方法,我也想使它成为一个纯虚函数,像intersect()以上。

但是,静态虚拟方法不是在C ++中允许的。 C ++静态虚拟成员? 并且我们可以有一个虚拟的静态方法? (C ++)问类似的问题,但他们不包括强制执行对派生类的功能的需求。

谁能帮我与上面​​?

Answer 1:

你可以有一个非静态的虚方法调用静态的一个(或返回一个静态字符串),在每个派生类中适当的执行。

#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()无论如何,每个子类中的方法,所以你能做的最好的是字符串是静态的。 但是,你可以考虑使用enum而不是字符串,也避免在你的代码不必要的字符串比较。

现在,回到了问题的基本面,我认为设计是有点瑕疵。 你真的不能有对各种形状的工作的一般交集功能,因为类型的交叉导致的形状差别很大,即使是同一类型的形状(两架飞机可以在一个平面上,一条线相交,或不相交于所有,例如)。 因此,在试图提供一个通用的解决方案,你会发现自己执行这些各种类型的检查所有的地方,这将unmaintainably增加更多的形状添加。



Answer 2:

让我们想想这个一秒钟。 我敢肯定,你不仅有2个sublcasses,让我们概括这一点。

浮现在脑海中的东西是重复代码,可扩展性和亲近。 让我们对这些扩展:

如果您想添加更多的类,你应该改变最少的代码放在可能的。

因为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);
};

我们已经决定,我们不希望内部的交叉点逻辑PlaneSphere ,所以我们创建了一个新的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&,...)

请记住,添加一个新的类时,我们并不想改变现有的代码。 因此,我们不能查看里面的交叉功能的类型。

我们可以为该(策略模式),其行为不同,这取决于A型行为的类,我们可以事后扩展:

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新类型的类

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



Answer 3:

因为他们在你提供的链接讨论的原因,你不能让一个虚拟成员静态的。

您对执行对派生类的功能的需求问题是通过在抽象基类,它会强制将派生类必须实现的函数的函数纯虚处理。



Answer 4:

作为同一类的所有实例都将返还相同种类,它是有道理的制式()的静态方法。

不,不是的。 您可以使用,当你不需要一个对象的实例调用该函数的静态方法。 在这种情况下,你要识别对象的类型,所以需要一个实例。

所有的方法体是由所有对象反正共享,所以没有必要担心重复。 唯一的例外是当函数是内联,但是编译器将尽最大努力,以尽量减少开销,并可能将其非内联如果成本太大。

PS需要一个类来标识自己的类层次结构之外,通常是一个坏的代码味道。 尝试寻找另一种方式。



文章来源: How to enforce a static member on derived classes?