我有一个基类, Primitive
,从中我推导几个其它classes-- Sphere
, Plane
等
Primitive
强制一些功能性,例如intersect()
通过纯虚函数它的子类。 的计算intersect
取决于实例数据,因此是有意义的具有它作为一个部件的方法。
我的问题出现在以下情况:我希望每一个派生的实例能够识别其类型,通过说std::string type()
成员方法。 作为同一类的所有实例都将返还相同种类,它是有道理的,使type()
一个static
方法。 正如我也希望每一个Primitive
子类实现这个方法,我也想使它成为一个纯虚函数,像intersect()
以上。
但是,静态虚拟方法不是在C ++中允许的。 C ++静态虚拟成员? 并且我们可以有一个虚拟的静态方法? (C ++)问类似的问题,但他们不包括强制执行对派生类的功能的需求。
谁能帮我与上面?
你可以有一个非静态的虚方法调用静态的一个(或返回一个静态字符串),在每个派生类中适当的执行。
#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增加更多的形状添加。
让我们想想这个一秒钟。 我敢肯定,你不仅有2个sublcasses,让我们概括这一点。
浮现在脑海中的东西是重复代码,可扩展性和亲近。 让我们对这些扩展:
如果您想添加更多的类,你应该改变最少的代码放在可能的。
因为intersect
操作是可交换的 ,相交的代码A
和B
应在相同的位置相交的代码B
和A
,因此保持类本身是不可能的内部逻辑。
此外,添加一个新的类,并不意味着必须修改现有的类,而是扩展委托类(是的,我们将要进入的模式在这里)。
这是当前的结构,我认为(或类似的,可能是返回类型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);
};
我们已经决定,我们不希望内部的交叉点逻辑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&);
};
在这里,您将添加新功能的类和新的逻辑。 例如,如果你决定增加一个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
新类型的类
我敢打赌,这还可以进一步完善。
因为他们在你提供的链接讨论的原因,你不能让一个虚拟成员静态的。
您对执行对派生类的功能的需求问题是通过在抽象基类,它会强制将派生类必须实现的函数的函数纯虚处理。
作为同一类的所有实例都将返还相同种类,它是有道理的制式()的静态方法。
不,不是的。 您可以使用,当你不需要一个对象的实例调用该函数的静态方法。 在这种情况下,你要识别对象的类型,所以你需要一个实例。
所有的方法体是由所有对象反正共享,所以没有必要担心重复。 唯一的例外是当函数是内联,但是编译器将尽最大努力,以尽量减少开销,并可能将其非内联如果成本太大。
PS需要一个类来标识自己的类层次结构之外,通常是一个坏的代码味道。 尝试寻找另一种方式。