簡體   English   中英

將基類的對象轉換為派生類

[英]Converting objects of base class to derived class

幾天前,我問過一些關於繼承的問題 ,我仍然想理解這個概念。 這是后續問題,因為我仍然面臨問題。

在我的項目中,我有兩種類型的對象,即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;

由手或頭對象組成。 在上一個問題中,我被告知這種方法是有意義的,我只需要使用boost static_pointer_cast從基類轉換為派生類即可。

現在,問題是對於向量中的某些對象,我不知道它們是Hand還是Head ,所以在我的代碼中的某些時候,我可以在cBodyPartscBodyParts一些Hand元素,一些Head元素以及一些BodyPart元素。 經過進一步分析,我能夠將后者正確分類為“ Hand或“ Head並相應地修改矢量中的元素,但是我不知道如何制作。 我是否只刪除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

使用虛函數,它們將簡化很多問題。

否則,您可以添加一些方法來區分不同的類型。 但是, 僅當您不能以其他方式執行此操作時 ,即無法通過虛函數執行操作時,才執行此操作。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM