2

次のようなクラスの構造があるとします。動物ベクトルの要素がどのクラスタイプであるかを判別できるようにしたいので、サブクラス固有のメソッドを実行できます。以下の例は、次のことを示しています。

#include <iostream>
#include <vector>

using namespace std;

class Animal {
    public:
    int foodcount;

    Animal() {
        foodcount = 0;
        cout << "An animal was created.\n";
    }
    virtual ~Animal() {
        cout << "An animal was destroyed.\n";
    }
};

class Lion : public Animal {
    public:
    Lion() {
        cout << "A lion was created.\n";
    }
    virtual ~Lion() {
        cout << "A lion was destroyed.\n";
    }
    void chowMeat(int howmuch) {
        foodcount += howmuch;
    }
};

class Butterfly : public Animal {
    public:
    Butterfly() {
        cout << "A butterfly was created.\n";
    }
    virtual ~Butterfly() {
       cout << "A butterfly was destroyed.\n";
    }
    void drinkNectar(int howmuch) {
       foodcount += howmuch;
    }
};

int main() {
    Animal* A = new Lion();
    Animal* B = new Butterfly();
    vector<Animal*> v;

    v.push_back(A);
    v.push_back(B);

    // a little later

    for (int i=0; i<v.size(); i++) {
        if (v[i] is a Lion) v[i]->chowMeat();  // will not work of course
        if (v[i] is a Butterfly) v[i]->drinkNectar();   // will not work of course
    }

    std::cin.get();
    return 0;
}

明らかに、マークされたコードは機能しませんが、どうすればやりたいことを実行できますか?従うべきであるが従わない回避策または設計原則はありますか?dynamic_castを調べましたが、それがきれいではないことを理解しています。では、どうすれば正しく行うことができますか?

Javaでは、これを行います。

if (v.get(i).getClass() == Lion.class) {
    ((Lion)v.get(i)).chowMeat();
}
if (v.get(i).getClass() == Butterfly.class) {
    ((Butterfly)v.get(i)).drinkNectar();
}
4

4 に答える 4

1

理想的には、基本クラスに仮想関数を追加void eat(int quantity)し、派生クラスでその関数をオーバーライドします。

この場合、両方の派生クラスがまったく同じことを行うため、関数を非仮想にして基底クラスに実装することも理にかなっています。

dynamic_castそれを除けば、オブジェクトの動的タイプをテストするために使用できます。

if (Lion* lion = dynamic_cast<Lion*>(v[i])) {
    lion->chowMeat(42); 
}
else if (Butterfly* butterfly = dynamic_cast<Butterfly*>(v[i])) {
    butterfly->drinkNectar(42);
}
// etc.

(別の注意として、C++ でネイキッド ポインターを使用する場合は非常に注意する必要があります。リソースを手動で管理する正しいコードを記述することは非常に困難です。この例では、 and が指すオブジェクトを解放していませAB。のようなスマート ポインターを使用してshared_ptr、リソースを自動的に管理することを検討してください)。

于 2010-12-08T00:42:10.813 に答える
0

食べてみませんか?

class Animal {
    public:
    int foodcount;

    Animal() {
        foodcount = 0;
        cout << "An animal was created.\n";
    }
    virtual ~Animal() {
        cout << "An animal was destroyed.\n";
    }
    virtual void eat(int howMuch) {
        foodcount += howmuch;
    }
};

class Lion : public Animal {
    public:
    virtual void eat(int howmuch) {
        Animal::eat(howmuch + 19);
    }
};

class Butterfly : public Animal {
    void eat(int howmuch) {
       Animal::eat(howmuch / 1000);
    }
};

class Tribble: public Animal
{
    void eat(int howmuch) {
       throw DontFeedTribles();
    }
};

int main() {
    std::auto_ptr<Animal> A = new Lion();
    std::auto_ptr<Animal> B = new Butterfly();
    vector<Animal*>  menagerie;

    menagerie.push_back(A.get());
    menagerie.push_back(B.get());

    BOOST_FOREACH(Animal* animal, menagerie)
    {
        animal->eat(10000);
    }

    std::cin.get();
    return 0;
}
于 2010-12-08T01:02:20.070 に答える
0

例が真に代表的なものである場合、仮想関数は差し迫った問題をはるかにうまく解決します。

いずれにせよ、クラスに仮想関数がある場合、最も簡単な答えはdynamic_cast、オブジェクトが特定のタイプであるかどうかを確認するために使用することです。例えば:

for (int i=0; i<v.size(); i++) {
    if (Lion *lion = dynamic_cast<Lion *>(v[i]))
        lion->chowMeat();
    else if(Butterfly *butterfly = dynamic_cast<Butterfly *>(v[i]))
        butterfly->drinkNectar();
}

これは言語に組み込まれており、ベースへのポインターが実際に派生型のオブジェクトを指しているかどうかを確認するためのものです。

もう 1 つのオプションは、基本クラスにある種の仮想 GetType 関数を用意することです。これをクラスごとにオーバーライドして、そのクラスを一意に識別する何か (整数、オブジェクトなど) を返します。次に、実行時にこの関数を呼び出し、結果を調べて、どのような種類のオブジェクトが指されているかを調べます。

dynamic_cast言語に組み込まれているという利点があり、サポートするためにあなたの側で努力する必要はありません。独自の関数を使用すると、さまざまなコンパイラでより予測可能なパフォーマンス特性が得られ、オブジェクトの実際の型以外のデータを格納できますが、すべてを自分で作成する必要があります。

于 2010-12-08T01:16:50.730 に答える
0

ループの目的は何ですか? 食べ物を消費することですか?その場合、virtual void consumeFood(int howMuch)基本クラスに a を追加し、派生クラスでそれをオーバーライドします。

于 2010-12-08T00:41:27.163 に答える