4

私は数日前に、私がまだ理解しようとしている概念である継承に関するいくつかの説明を求めました。私はまだ問題に直面しているので、これがフォローアップの質問です。

私のプロジェクトでは、Hand と Face の 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;

Hand または Head オブジェクトで構成されます。前の質問で、このアプローチは理にかなっていると言われました。ブーストを使用して基本クラスから派生クラスにキャストする必要がありましたstatic_pointer_cast

Handさて、問題は、ベクター内のオブジェクトの一部について、それらが であるかであるかがわからないHeadため、コードのある時点で、cBodyPartsいくつかのHand要素、いくつかのHead要素、およびいくつかの要素を使用できることBodyPartです。さらに分析した後、後者を正しく分類するかHandHeadベクトル内の要素をそれに応じて変更することができますが、それを作成する方法がわかりません。ケース クラス要素を削除して、同じプロパティを持つ派生要素を作成しますか? このような場合は、継承を避けるべきですか?

助けてくれてありがとう

4

4 に答える 4

4

EDIT:より明確にするために例を増やしました。

キャストで中継することは、通常、設計が悪いことを示しています。キャストにはそれぞれの場所がありますが、これはそうではないようです。

に格納されているオブジェクトをどうしたいか自問する必要がありますcBodyPartsHand確かに、または を使用して別のことを行うことになります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
}

以前に書いたコードを変更する必要はありません。上記のループは and or it でfor正しく動作し、更新の必要はありません。TorsoLeg

于 2013-01-09T10:32:59.287 に答える
2

股関節の骨は太ももの骨につながっています...

私はあなたが体のすべての部分、おそらくBodyクラスの合成物を持っていると思います。

体はどうしたいの?

  • レンダリング自体
  • シリアライズ
  • ボリューム、バウンディング ボックス、またはその他のメトリックを出力します。
  • 入力に応じて向きを変える
  • 逆運動学的物理モデルに対応する

リストはおそらく続く可能性があります。で何をしたいのかが正確にわかっている場合はBody、その関数をBodyPart基本クラスに配置Bodyし、接続されたすべてのボディパーツの複合階層構造を反復処理して、renderたとえば を呼び出すことができます。

代わりに、 を使用することもできますVisitor。これは、メソッドを静的継承階層に動的に追加する効果的な方法です。

于 2013-01-09T11:08:50.850 に答える
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 に答える