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

Converting objects of base class to derived class

本文关键字:派生 转换 对象 基类      更新时间:2023-10-16

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

在我的项目中,我有2种类型的对象,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();
}

while 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

从基类转换到派生类

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

提前感谢您的帮助

编辑:我已经增加了这些例子,使它们更清晰。

依赖强制类型转换通常是糟糕设计的标志。强制类型转换有它的位置,但看起来不是这样。

你需要问自己,你想用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一起正常工作,而不需要更新

髋骨与大腿骨相连…

我想如果你有一些身体部位的合成物,也许是Body类。

你希望正文做什么?

  • 渲染本身
  • 连载
  • 输出其体积,或边界框,或其他度量
  • 根据输入
  • 重新定位
  • 响应逆运动学物理模型

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

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

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

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

Else,您可以添加一些方法来区分不同的类型。但是,只在不能用其他方法时使用,即不能通过虚函数来使用。

示例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; }