4

几天前我问了一些关于继承的澄清,我仍在努力理解这个概念。这是后续问题,因为我仍然面临问题。

在我的项目中,我有 2 种类型的对象,手和脸,它们都继承自基类 BodyPart。BodyPart 是这样的:

class BodyPart
{
  public:
  typedef boost::shared_ptr<BodyPart> BodyPartPtr;

  BodyPart();
  virtual ~BodyPart();

  private:
  int commonMember1;
  double commonMember2;

  public:
  int commonMethod1();
  int CommonMethod2();
}

而 Hand 是这样的:

class Hand : public BodyPart
{
  public:
  Hand();
  ~Hand();

  private:
  int numFingers;
  double otherVar;

  public:
  int getNumFingers();
  void printInfo();
}

我还有一个 BodyPart 元素的向量

std::vector<BodyPart::BodyPartPtr> cBodyParts;

由手或头对象组成。在上一个问题中,有人告诉我这种方法很有意义,我只需要使用 boost 从基类转换为派生类static_pointer_cast

现在,现在的问题是,对于向量中的某些对象,我不知道它们是Handor Head,所以在我的代码中的某个时刻,我可以在cBodyParts一些元素中包含一些Hand元素,一些Head元素以及一些BodyPart元素。经过一些进一步的分析,我能够正确地将后者分类为HandorHead并相应地修改向量中的元素,但我不知道如何制作它。我应该删除案例类元素并创建一个具有相同属性的派生类元素吗?在这种情况下我应该避免继承吗?

在此先感谢您的帮助

4

4 回答 4

4

编辑:我增加了示例以使它们更清晰。

在演员表上接力通常是糟糕设计的标志。演员有他们的位置,但这看起来不是。

你需要问问自己你想对存储在cBodyParts. 当然,你会用 aHand或 a做不同的事情Head,但你可能会以某种方式抽象它们:这就是虚函数的作用。因此,除了您已经为您的类编写的内容之外,您只需要在其中添加一个虚拟函数:

class BodyPart
{
  // Same as you wrote, plus:
public:
  virtual void InitialisePart() = 0; // Pure virtual: each body part must say how to process itself
  virtual void CalibrateJoints() {} // Override it only if the body part includes joints
}

class Head : public BodyPart
{
  // Same as you wrote, plus:
public:
  virtual void InitialisePart() {
    // Code to initialise a Head
  }
  // Since a Head has no joints, we don't override the CalibrateJoints() method
}

class Hand : public BodyPart
{
  // Same as you wrote, plus:
public:
  virtual void InitialisePart() {
    // Code to initialise a Hand
  }
  virtual void CalibrateJoints() {
    // Code to calibrate the knuckles in the hand
  }
}

然后你不再需要任何演员表。例如:

for (BodyPart::BodyPartPtr part : cBodyParts) {
  part->InitialisePart();
  part->CalibrateJoints(); // This will do nothing for Heads
}

如您所见,根本没有演员表,一切都会正常工作。该方案是可扩展的;如果您后来决定需要其他继承自 的类BodyPart,只需编写它们,您的旧代码就会正常工作:

class Torso : public BodyPart
{
public:
  virtual void InitialisePart() {
    // Code to initialise a Torso
  }
  // The Torso has no joints, so no override here for CalibrateJoints()

  // Add everything else the class needs
}

class Leg : public BodyPart
{
public:
  virtual void InitialisePart() {
    // Code to initialise a Leg
  }
  virtual void CalibrateJoints() {
    // Code to calibrate the knee
  }

  // Add everything else the class needs
}

现在您不需要更改您之前编写的代码:for上面的循环将与 andTorsoLeg它的 find 一起正常工作而无需更新。

于 2013-01-09T10:32:59.287 回答
2

髋骨连接到大腿骨...

我认为你有所有身体部位的复合体,也许是一个Body班级。

你想让身体做什么?

  • 渲染自己
  • 序列化
  • 输出它的体积、边界框或其他一些指标
  • 重新定位自己以响应输入
  • 响应逆运动物理模型

这份名单可能会继续下去。如果你确切地知道你想要Body做什么,你可以将该函数放在BodyPart基类中,并Body迭代所有连接的身体部分的复合层次结构,render例如调用。

另一种方法是使用 a Visitor,这是一种有效地将方法动态添加到静态继承层次结构的方法。

于 2013-01-09T11:08:50.850 回答
0

正如Kerrek SB 指出的那样,这根本不可行,但是为了回答实际问题,dynamic_cast这就是您要寻找的。

于 2013-01-09T10:10:37.943 回答
0

使用虚函数,它们会大大简化你的问题。

否则,您可以添加一些方法来区分不同的类型。但是,只有在您无法以其他方式执行此操作时才执行此操作,即您无法通过虚拟函数执行此操作。

示例 1:

// in BodyPart; to be reimplemented in derived classes
virtual bool isHand() const { return false; }
virtual bool isHead() const { return false; }

// in Hand (similar to what will be in Head)
bool isHand() const { return true; }

// How to use:
BodyPart::pointer ptr = humanBodyVector[42]; // one item from the array
if(ptr->isHand())
    processHand(/*cast to hand*/)
else if(ptr->isHead())
    // ...

示例 2:让派生类处理强制转换

// in BodyPart; to be reimplemented in derived classes
virtual Hand* toHand() const { return 0; }
virtual Head* toHead() const { return 0; }

// in Hand (similar to what will be in Head)
Hand* toHand() const { return this; }
于 2013-01-09T10:13:30.520 回答