将基类的对象转换为派生类

时间:2013-01-09 10:04:05

标签: c++ oop inheritance polymorphism smart-pointers

我几天前问了一些clarifications on inheritance这个我仍在努力理解的概念。这是后续问题,因为我仍然面临问题。

在我的项目中,我有两种类型的对象,Hand和Face,都继承自基类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;

由Hand或Head对象组成。在上一个问题中,我被告知这种方法是有道理的,我只需要使用boost static_pointer_cast

从基类转换为派生类。

现在问题是,对于向量中的某些对象,我不知道它们是Hand还是Head,所以在我的代码中的某些时候我可以在{ {1}}一些cBodyParts元素,一些Hand元素以及一些Head元素。经过一些进一步的分析后,我能够正确地将后者归类为BodyPartHand,并相应地修改向量中的元素,但我不知道如何制作它。我应该删除case类元素并创建一个具有相同属性的派生元素吗?如果是这样的话,我应该避免继承吗?

提前感谢您的帮助

4 个答案:

答案 0 :(得分:4)

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

转换角色通常是设计糟糕的标志。演员有他们的位置,但这看起来不是。

您需要问自己,您想对cBodyParts中存储的对象做什么。当然,你将使用HandHead做不同的事情,但你可能会以某种方式抽象它们:这就是虚函数的作用。因此,除了您已经为类编写的内容之外,您还需要一个额外的虚函数:

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循环可以正常使用,而TorsoLeg找不到更新。

答案 1 :(得分:2)

髋骨与大腿骨相连......

我认为你有一些所有身体部位的复合物,也许是Body类。

你想让身体做什么?

  • 渲染自己
  • 连载
  • 输出其音量,边界框或其他指标
  • 根据输入重新定位自己
  • 回应反向运动物理模型

列表可能会继续下去。如果您确切知道Body要做什么,可以将该函数放在BodyPart基类中,并让Body遍历所有连接的正文部分的复合层次结构,例如,调用render

另一种方法是使用Visitor,这实际上是一种动态添加方法到静态继承层次结构的方法。

答案 2 :(得分:0)

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

答案 3 :(得分: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; }