3

followigクラスをしましょう

class Animal
{
  public: 
    virtual short getFeet() = 0;
    virtual void setFeet(short feet) = 0;
};

および2つの派生クラス:

class Bird : public Animal
{
    short flying_speed; 
  public:
    virtual short getFeet() { return 2; }  // always 2 feet
    virtual void setFeet(short feet_) { };
    virtual short getFlyingSpeed() { return flying_speed; }
};

class Mammal : public Animal 
{
    short feet; // whale: 0 feet, human: 2 feet, others: 4 feet
  public:
    virtual short getFeet() { return feets; }
    virtual void setFeet(short feet_) { feet = feet_ };
};

A)オブジェクトモデルに関連する質問:

プロパティの足に焦点を当てましょう。アトリビュートフィートとメソッドgetFeet()およびsetFeet()をどうするか。

同じカテゴリに属する​​一部の動物は足の量が異なるため、属性の足とメソッドを使用し、getFeet()一般setFeet()的です。

一部の動物は同じ足の量を持っているため、独自の属性を使用しませんfeet。メソッドgetFeet()は定数を返しますが、メソッドsetFeet()は何もしません。

このモデルは正しいですか、それとも可変足に関連する変更が推奨されていますか(足のない動物がたくさんいます)?

B)多型に関する質問

1つのカテゴリには特定の機能があります。たとえば、飛んでいる鳥。したがって、彼らが飛んでいる速度を尋ねるのは理にかなっています。方法を参照してくださいgetFlyingSpeed()

Birdポリモーフィズムを使用して、 :を指すポインターを作成します。

int main(int argc, _TCHAR* argv[])
{
    Animal *a = new Bird();
    std:: cout << a->getFlyingSpeed() << '\t'; // error!
}

コンパイラはコンパイル中ではなく正しい割り当てをチェックすると思いましたが、両方のタイプのランタイムチェックしかありません。しかし、私は間違っていました...

エラーC2039::はのgetFlyingSpeedメンバーではありませんAnimal

すべてのクラスに共通ではない属性をポリモーフィズムと一緒に使用する方法はありますか?または、ランタイムタイプのchcekのみを強制できますか?

たぶん、このモデルは正しくありません。それをやり直す方法は?

ご協力いただきありがとうございます。

4

7 に答える 7

2

呼び出し動物は、すべての動物(またはアプリケーションで使用したい動物)に共通する機能のみを提供する必要があります。すべての動物に共通ではない他の機能については、多重継承を使用して、サブタイプの動物に望ましい動作を実装できます。したがって、サブタイプはFlyingAnimalsである可能性があります。

class FlyingAnimal
{
  public: 
    virtual short getFlyingSpeed() = 0;
    virtual ~FlyingAnimal(){}

};


class Bird : public Animal, public FlyingAnimal
{
    short flying_speed; 
  public:
    virtual short getFeet() { return 2; }  // always 2 feet
    virtual void setFeet(short feet_) { };
    virtual short getFlyingSpeed() { return flying_speed; }
};

足のある動物などのサブクラスを定義することもできます。

または、多重継承の代わりに、次のように構築できます。

class FlyingAnimal: public Animal
{
  public: 
    virtual short getFlyingSpeed() = 0;
    virtual ~FlyingAnimal(){}

};


class Bird : public FlyingAnimal
{
    short flying_speed; 
  public:
    virtual short getFeet() { return 2; }  // always 2 feet
    virtual void setFeet(short feet_) { };
    virtual short getFlyingSpeed() { return flying_speed; }
};

ただし、主な方法では、疑う動物の種類を定義する必要があります。

int main(int argc, _TCHAR* argv[])
{
    FlyingAnimal *a = new Bird();
    std:: cout << a->getFlyingSpeed() << '\t'; // not an error anymore!
}
于 2012-08-13T19:39:57.717 に答える
2
Animal *a = new Bird();

割り当ては正しいです。Birdはから派生しAnimalているため、Bird*はに格納できますAnimal*。ただし、ポインターのタイプは、へのポインターaではAnimalなく、へのポインターBirdであるため、に対して定義されているメンバー関数のみを呼び出すことができますAnimal。そのため、コンパイラはに反対します

a->getFlyingSpeed()

getFlyingSpeedのメンバー関数ではありませんAnimal。一方、呼び出し

a->getFeet()

getFeetはのメンバー関数なので、大丈夫ですAnimal。が指すオブジェクトのタイプであるため、でgetFeet定義されているバージョンを呼び出します。BirdBirda

つまり、C++は静的に型付けされています。つまり、型はコンパイル時に決定されるため、でのみAnimalメンバー関数を呼び出すことができますa

于 2012-08-13T19:43:56.687 に答える
2

A)

公開setFeet()は奇妙に見えますが(まあ、これはアマチュア外科医のサンドボックスだとしましょう)、オブジェクト階層を次のように再設計することをお勧めします。

class Animal
{
public:
    virtual short getFeet() const = 0;
}

class SurgeonDelight : public Animal
{
    short feetNumber;
public:
    short getFeet() const {return feetNumber;}
    virtual void setFeet(short feet) {feetNumber = feet;}
}

次に、から一定の足数を持つすべての動物を継承しAnimal、他のすべての動物をから継承しますSurgeonDelightsetFeetその子孫が足の数を変更することによるいくつかの副作用を実装できるように仮想化します。SurgeonDelight実際のデータメンバーを非表示にして、「変更する単一の理由」を強制します。

B)

RTTIとを使用できますdynamic_castが、基本的にはBirdBird

誰かが「dynamic_castをしなければならないのなら、あなたのデザインは悪い」と私に言いました。私は同意する傾向があります。私は5年以上コーディングしdynamic_cast、一度も使用しませんでした(サポートしていなかったため)。動物園に飛行速度のある動物がもっといる場合は(のようにTurtle)、階層に一般化の追加レイヤーを挿入する必要があります。

いくつかのライブラリで見たもう1つのあまり良くないアプローチは、「拡張」APIを残すことです。

virtual void* extension(int functionId, int param) = 0;

うん、かなり醜いですが、そこにあなたを連れて行きます

于 2012-08-13T19:44:42.163 に答える
1

ここにはいくつか間違っていることがあります。

まず、インスタンスfeetの存続期間中にプロパティを変更できることは意味がありません。Animal人間が2フィート持っていても、突然3分の1または77番目のフィートを成長させることはできません。また、同じ動物タイプのすべてのインスタンスで同じである必要があります。したがってfeet、インスタンスごとに設定可能ではなく、クラスごとに修正する必要があります。

これを試して:

class Animal
{
  private:
    short _feet;
  protected:
    Animal(short feet) : _feet(feet) { }
  public:
    short getFeet() { return _feet; }  // and call it 'getNumberOfFeet`; see below.
                                       // note: no longer virtual!
};

class Whale : public Animal
{
  public:
    Whale() : Animal(0) { }
};

class Mammal : public Animal { … };

class Ape : public Mammal
{
  public:
    Ape() : Animal(2) { }
};

(エラーがいくつかある場合は申し訳ありません。最近の私のC ++スキルは少し錆びています。)

また、すべての類人猿の足数を4に設定したことにも注意してください。これは、クラスHumanApe(進化論を信じていると仮定して...)から派生し、 Ape4フィートある場合、それは理にかなっているためです。人間には2つしかなかったと言ってこれと矛盾するのですか?Humansは明らかに適切ではないため、型階層に問題がありますApe

numberOfFeetところで、あなたのプロパティがではなく、と呼ばれた方が理にかなっていますfeet。を使用すると、または同様の何かfeetを取り戻すことが期待されます。std::vector<Foot>結局のところ、誰かがあなたに「あなたの足をください」と言うとき、あなたはあなたの足を彼らの膝か何かに置くでしょう、しかしあなたは「2つ!」とは言わないでしょう。...あなたは?


次に、コード例:

int main(int argc, _TCHAR* argv[])
{
    Animal *a = new Bird();
    std:: cout << a->getFlyingSpeed() << '\t'; // error!
}

2つの可能性:

  • あなたはいつもそれがをa参照していることを知っていBirdます。では、なぜ変数aをとしてAnimal*ではなくとして宣言するのBird*ですか?

  • aを参照する場合としない場合がありBirdます。では、なぜあなたは電話するのa->getFlyingSpeed()ですか?aインスタンスでない場合はどうなりBirdますか?

考えられる解決策の1つ:

class Animal
{
  public:
    void explainYourself() = 0;
};

class Bird : public Animal
{
    …
  public:
    void explainYourself()
    {
        std::cout << "I am flying at " << getFlyingSpeed() << " mph." << std::endl;
    }

};

class Mammal : public Animal
{
    …
  public:
    void explainYourself()
    {
        std::cout << "Hello, I have " << getFeet() << " feet." << std::endl;
    }
};


int main(…)
{
    Animal *a = new Bird();
    a->explainYourself();
    return 0;
}
于 2012-08-13T19:45:40.633 に答える
1

これは豊富なトピックです。この残忍な解決策を考えてみましょう。

struct FlyingSpeed {
  bool meaningful;
  short speed;
  static const FlyingSpeed nofly; 
};

const FlyingSpeed FlyingSpeed::nofly = { false, 0 };

Flying_speedメソッドがshortではなくFlyingSpeedタイプのオブジェクトを返した場合。Animalの各インスタンスは、FlyingSpeedオブジェクトを返すことでgetFlyingSpeedメソッドに応答できます。鳥、コウモリ、ミツバチは意味のあるFlyingSpeedを持っているでしょう。ワーム、オオカミ、セイウチは意味のないFlyingSpeedを持っているでしょう。カンガルーは、ホッピングの見方によっては、飛行速度がある場合とない場合があります。蜘蛛は、バロンをしている間は意味のあるFlyingSpeedを持ち、ウェブを構築すると意味のないFlyingSpeedを持ちます。

クラスオブジェクトnoflyは、陸生動物、樹木動物、水生動物が簡潔なコンストラクターを持つことができるようにするための便利な機能です。

于 2012-08-13T19:50:55.450 に答える
0

A)本当に許可したいのでなければ、すべての動物が一度作成された足の数を変更することはできないと思います。したがって、feetAnimalのコンストラクターで定義し、関連するセッターを削除するのが最善です。その後、Animalのサブクラスは、問題なくベースコンストラクターを呼び出すことができます。動物に足がまったくないことを示すのは通常理にかなっているので、私は足のある動物のためのクラスを捧げる価値があるとは思いません。

B)提案されていることが最も適切です:飛んでいる動物のためのインターフェースクラス(純粋に仮想メソッドを使用)を作成します。それを呼び出すと、ポインタをFlyingAnimal使用してメソッドを呼び出すことができます。FlyingAnimal*動物が実行時に飛ぶことができるかどうかを知りたい場合は、bool canFly()メソッドを使用して、動的キャストを安全にすることができます。

于 2012-08-13T19:44:46.607 に答える
-1

実行時にどのタイプであるかを次のように確認できますdynamic_cast

void tryFlying(Animal *a) {
    Bird *b = dynamic_cast<Bird *>(a);
    if (b) {
        std::cout << "uninitialized speed junk " << b->getFlyingSpeed() << std::endl;
    } else {
        std::cout << "can't fly" << std::endl;
    }
}

int main() {
    Animal *a, *b;
    a = new Bird;
    b = new Mammal;
    tryFlying(a);
    tryFlying(b);
    return 0;
}

出力:

 uninitialized speed junk -27757
 can't fly

空きメモリリークが発生します(仮想デストラクタが必要です)。

于 2012-08-13T19:42:46.143 に答える