Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 添加虚拟函数而不修改原始类_C++_Oop_Polymorphism - Fatal编程技术网

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 >
}