1

私はいくつかの異なるオブジェクトを持っていますZooが、それらはすべてタイプが で、メンバー変数がありますCollection = vector<Animal>。すべての要素が 動物 である Zoo のインスタンスが 1 つありBirdsますBats。との両方が動物BirdsBats由来します。

すべての鳥とコウモリを呼び出すことができるメソッドが必要fly()ですが、これを行う最善の方法がわかりません。鳥/コウモリ動物園をループするときにキャストする必要がありますか? このような:

Bird thisBird = static_cast<Bird>(Collection[i]);
thisBird.fly();

...または、アニマルから派生したクラスにのみ実装される仮想関数をどうにかしてアニマルに持たせることはできますか?

それらが「良いコード」である理由のアイデアと正当化を歓迎します!

4

3 に答える 3

1

すべての動物が空を飛べるわけではないので、一般的な目的で特定の行動 (つまり、すべての動物で珍しい) を実行しようとしても意味がありません。Animal クラス (またはその派生クラス) のコレクションがある場合、それらが共通の動作を実行できるようにするためです (派生クラスごとに異なる方法で実装されます)。

インターフェイス (つまり、抽象クラス) を実装し、FlyAble実際に空を飛べる動物だけがそれを拡張できます。これらの種類の動物用に指定された別のコレクション (つまり < FlyAble>) にそれらを保存するだけではありません。

于 2013-04-12T11:33:03.757 に答える
1

おそらく、配信元の下位クラスでメソッドが宣言されていないような階層は必要ありませんBird。そうすれば、統一された階層の要点を失うだけです。BatAnimalfly()

次のいずれかを実行できます。

class Animal {
    virtual bool canFly() { return false; }
    virtual void fly() {throw Exception( "Sorry babe, I don't fly"); }
}

とでオーバーライドflyします。BirdBat

fly()これにより、おそらく望まない犬や猫用のメソッドが実装されることになります。Flyerしたがって、このメソッドを宣言する新しいクラスを作成できます。

class Flyer : public Animal {
    virtual void fly() = 0;
}

class Bat : public Flyer {}
class Bird : public Flyer {}

Reptileこれは、のようなより詳細な生物学的分類と矛盾しますMammal

もう 1 つのトリックは、 のようなメソッドを提案しmove()、犬はそれを として実装しrun()、鳥は として実装しfly()、すべて統一されたインターフェイスを使用することです。

もう1つは、犬が飛べるかどうかを尋ねるのは有効な質問だと思うので Dog.canFly()、アニマルの一部としてコードにメソッドを実装する必要があると思います。

これらすべてを考慮して、私はこれに行きます:

// Your base animal
class Animal {
    virtual bool canFly() {return false;}
};

// Any animal that could fly, just abstract class
class Flyer {
    virtual void fly() = 0;
}

// Ants, flies etc.; real biological hierarchy
class Insect : public Animal {}
class Mammals : public Animals {}
class Birds : public Animals {}

// And now flying bird :
class Bird : public Birds, public Flyer {
    virtual bool canFly() {return true; }
    virtual void fly() {...}
}

// And flying insect
class Fly : public Insect, public Flyer {
    virtual bool canFly() {return true; }
    virtual void fly() {...}
}

そして、あなたはそれを行うことができます(この回答に基づいて、これにはポインターを使用する必要があると思います):

if( Collection[i]->canFly()){
    Flyer *thisBird = static_cast<Flyer*>(Collection[i]);
}

仮想継承から派生Flyerさせて使用することもできますが、これは「恐ろしいダイヤモンド」と呼ばれ、良い習慣とは見なされませんが、読む価値があります。Animal


コメントでRicibobが指摘したように、Flyerを指定する必要がありますcanFly()。これは、簡単な例では次のようになります。

class Flyer {
public:
    virtual bool canFly() const {return true;}
    virtual void fly() {cout << "Flying" << endl; }
};

Bird b;
cout << "Can fly: " << b.canFly() << endl;
// Error    1   error C2385: ambiguous access of 'canFly'

しかし、クラスとクラスの一部としてFlyer配信するAnimalと、次のようになります。BirdBirdsFlyer

class Animal {
public:
    virtual bool canFly() const {return false;}
};

class Flyer : virtual Animal {
public:
    virtual bool canFly() const {return true;}
    virtual void fly() {cout << "Flying" << endl; }
};

class Bird : virtual public Birds, virtual public Flyer {

}; 

// Warning  1   warning C4250: 'Bird' : inherits 'Flyer::Flyer::canFly' via dominance

canFly()を返すようになりまし1たが、コードが間違っているようです。

この時点で...canFly()各サブクラス (または大規模なグループ) に対して手動で指定するか、または配信元(たとえば、これは適切ではありませんBirds) 、または新しいサブクラスを配信することができます。FlyersChicken

class FlyingBrids : public Birds, public Flyer /* Flyer not delivered from Animal */ {
    virtual bool canFly() const {return true;}
};

から配信されるFlyerため、 は依然として重要であることに注意してください。FlyInsect

于 2013-04-12T11:41:44.197 に答える
1

ああ、そうです、Animal仮想関数を持つことができます:

virtual void move(){}
virtual void fly(){}

犬に空を飛ばせと命じても何もしない。鳥に移動を命令すると、鳥も鳴くことがありますfly()

fly()飛ぶ動物を正しく 定義Colection[i]->fly()し、正しいことをするだけです。このようにして、コードは単純になります。

しかし、それを行うには、コレクションが Animal オブジェクトだけでなく、動物へのポインターを収集する必要があります。例えば:

#include <vector>
#include <memory>
#include <iostream>  
using namespace std;
struct Animal{virtual void fly(){}};
struct Bird:public Animal{void fly(){cout<<"fly\n";}};
int main()
{
  vector<unique_ptr<Animal>> Colection(1);
  Colection[0].reset( new Bird ); 
  Colection.push_back(unique_ptr<Animal>(new Bird) );
  Colection.push_back(unique_ptr<Animal>(new Animal) );
  Colection[0]->fly(); 
  Colection[1]->fly(); 
  Colection[2]->fly();   
}

他の回答を読んで、次のようなものを実装することをお勧めします

struct Animal    {virtual bool fly(){return false;     }};
struct Bird:public Animal{bool fly(){cout<<"fly\n";return true;}};

また、個別の canFly は必要ありません。

于 2013-04-12T12:17:21.380 に答える