C++ 添加虚拟函数而不修改原始类
假设我们已经有了一个类的层次结构,例如C++ 添加虚拟函数而不修改原始类,c++,oop,polymorphism,C++,Oop,Polymorphism,假设我们已经有了一个类的层次结构,例如 class Shape { virtual void get_area() = 0; }; class Square : Shape { ... }; class Circle : Shape { ... }; etc. 现在让我们假设我想(有效地)在每个子类中添加一个virtualdraw()=0方法到Shape。但是,假设我不想修改这些类(因为它们是我不想更改的库的一部分) 最好的办法是什么 我是否真的“添加”了一个virtual方法并不重要,我只需
class Shape { virtual void get_area() = 0; };
class Square : Shape { ... };
class Circle : Shape { ... };
etc.
现在让我们假设我想(有效地)在每个子类中添加一个virtualdraw()=0
方法到Shape
。但是,假设我不想修改这些类(因为它们是我不想更改的库的一部分)
最好的办法是什么
我是否真的“添加”了一个virtual
方法并不重要,我只需要给定一系列指针的多态行为
我的第一个想法是:
class IDrawable { virtual void draw() = 0; };
class DrawableSquare : Square, IDrawable { void draw() { ... } };
class DrawableCircle : Circle, IDrawable { void draw() { ... } };
然后用DrawableSquare
s和drawablescircle
s分别替换所有创建的Square
s和drawablescircle
s
这是实现这一点的最佳方法,还是有更好的方法(最好是保持正方形
s和圆形
s的创建完好无损的方法)
提前感谢。您所描述的有点像。这非常适合更改现有类的运行时行为 但我真的不知道如何实现您的实际示例,如果形状无法绘制,那么在运行时也无法更改绘制行为 但我想这只是stackoverflow的一个非常简单的例子?如果所需功能的所有基本构建块都可用,那么使用这种模式实现精确的运行时行为肯定是一个不错的选择。(我确实提出了一个解决方案……请耐心等待……) 一种(几乎)解决问题的方法是使用访问者设计模式。大概是这样的:
class DrawVisitor
{
public:
void draw(const Shape &shape); // dispatches to correct private method
private:
void visitSquare(const Square &square);
void visitCircle(const Circle &circle);
};
DrawVisitor::draw(const Shape &shape)
{
shape.accept(*this);
}
然后,与此相反:
Shape &shape = getShape(); // returns some Shape subclass
shape.draw(); // virtual method
你会做:
DrawVisitor dv;
Shape &shape = getShape();
dv.draw(shape);
通常在访问者模式中,您将实现如下draw
方法:
class DrawVisitor
{
public:
void draw(const Shape &shape); // dispatches to correct private method
private:
void visitSquare(const Square &square);
void visitCircle(const Circle &circle);
};
DrawVisitor::draw(const Shape &shape)
{
shape.accept(*this);
}
但只有当Shape
层次结构被设计为可访问时,这才有效:每个子类通过调用访问者上相应的visitXxxx
方法来实现虚拟方法accept
。很可能它不是为这个而设计的
在无法修改类层次结构以将虚拟的accept
方法添加到Shape
(以及所有子类)的情况下,您需要一些其他方法来分派到正确的draw
方法。一种方法是:
DrawVisitor::draw(const Shape &shape)
{
if (const Square *pSquare = dynamic_cast<const Square *>(&shape))
{
visitSquare(*pSquare);
}
else if (const Circle *pCircle = dynamic_cast<const Circle *>(&shape))
{
visitCircle(*pCircle);
}
// etc.
}
还有一个虚拟方法ShapeId-Shape::getId()const=0
每个子类将重写以返回其ShapeId
。然后,您可以使用大量的switch
语句来执行分派,而不是dynamic\u cast的if-elsif。或者使用哈希表代替开关。最好的情况是将此映射函数放在一个位置,这样您就可以定义多个访问者,而无需每次重复映射逻辑
因此,您可能也没有getid()
方法。太糟糕了。获取每种类型对象的唯一ID的另一种方法是什么?RTTI。这并不一定优雅或简单,但您可以创建一个包含type\u info
指针的哈希表。您可以在一些初始化代码中构建此哈希表,也可以动态构建(或两者兼而有之)
一个你可以考虑的“墙外”解决方案,取决于环境,是使用模板来给你编译时间的多态行为。在您发言之前,我知道这不会给您提供传统的运行时多态性,因此它可能不太有用,但根据您工作环境的限制,它可能会被证明是有用的:
#include <iostream>
using namespace std;
// This bit's a bit like your library.
struct Square{};
struct Circle{};
struct AShape{};
// and this is your extra stuff.
template < class T >
class Drawable { public: void draw() const { cout << "General Shape" << endl; } };
template <> void Drawable< Square >::draw() const { cout << "Square!" << endl; };
template <> void Drawable< Circle >::draw() const { cout << "Circle!" << endl; };
template < class T >
void drawIt( const T& obj )
{
obj.draw();
}
int main( int argc, char* argv[] )
{
Drawable<Square> a;
Drawable<Circle> b;
Drawable<AShape> c;
a.draw(); // prints "Square!"
b.draw(); // prints "Circle!"
c.draw(); // prints "General Shape" as there's no specific specialisation for an Drawable< AShape >
drawIt(a); // prints "Square!"
drawIt(b); // prints "Circle!"
drawIt(c); // prints "General Shape" as there's no specific specialisation for an Drawable< AShape >
}
#包括
使用名称空间std;
//这个有点像你的图书馆。
结构方{};
结构圆{};
结构AShape{};
//这是你多余的东西。
模板
类可绘制{public:void draw()常量{cout这不是一个简单的例子。我确实有一些形状想要添加绘图方法。具体来说,这些形状来自Bullet Physics SDK碰撞形状,我希望能够添加绘图功能以进行调试。如果我错了,请纠正我,但我不认为decorator解决了这个问题。decorator添加了将新的行为树添加到现有类中,而不是将行为添加到现有树中。Bullet SDK似乎有一个btIDebugDraw
接口:。您从中派生一个类,该类实现了drawBox()
和drawSphere()等函数
,然后将其分配到btCollisionWorld
或btDynamicsWorld
,然后让其绘制该世界。这样做可以满足您的需要吗?您的意思是访问圆(const Circle&Circle)
而不是visitSquare there?有趣的解决方案,我喜欢只使用部分访问者
模式>>模式是为了适应情况,而不是相反:)
#include <iostream>
using namespace std;
// This bit's a bit like your library.
struct Square{};
struct Circle{};
struct AShape{};
// and this is your extra stuff.
template < class T >
class Drawable { public: void draw() const { cout << "General Shape" << endl; } };
template <> void Drawable< Square >::draw() const { cout << "Square!" << endl; };
template <> void Drawable< Circle >::draw() const { cout << "Circle!" << endl; };
template < class T >
void drawIt( const T& obj )
{
obj.draw();
}
int main( int argc, char* argv[] )
{
Drawable<Square> a;
Drawable<Circle> b;
Drawable<AShape> c;
a.draw(); // prints "Square!"
b.draw(); // prints "Circle!"
c.draw(); // prints "General Shape" as there's no specific specialisation for an Drawable< AShape >
drawIt(a); // prints "Square!"
drawIt(b); // prints "Circle!"
drawIt(c); // prints "General Shape" as there's no specific specialisation for an Drawable< AShape >
}