2

より明確にするために、これを例の形で表現します。

動物のベクトルがあり、配列を調べて、要素が犬か猫かを確認したいとしますか?

class Dog: public Animal{/*...*/};
class Cat: public Animal{/*...*/};

int main()
{
vector<Animal*> stuff;
//cramming the dogs and cats in...

for(/*all elements in stuff*/)
//Something to the effect of:  if(stuff[i].getClass()==Dog) {/*do something*/}

}

それがある程度明確であることを願っています。typeid については知っていますが、比較する Dog オブジェクトが実際にはありません。できれば、Dog オブジェクトを作成したくありません。

これを行う方法はありますか?前もって感謝します。

4

7 に答える 7

6

他の人が指摘したように、ポインタが指すものの動的な型を取得するためにtypeid、または演算子を使用しないでください。dynamic_castこの種の不快感を避けるために、仮想関数が作成されました。

とにかく、本当にやりたい場合は、次のことを行います(イテレータを逆参照すると が得られることに注意してくださいAnimal*。したがって、そうすると、 が得られ**itますAnimal&):

for(std::vector<Animal*>::iterator it = v.begin(); it != v.end(); ++it) {
    if(typeid(**it) == typeid(Dog)) {
        // it's a dog
    } else if(typeid(**it) == typeid(Cat)) {
        // it's a cat
    }
}

typeid上記のように、型自体にも演算子を適用できることに注意してください。このためのオブジェクトを作成する必要はありません。また、のようなポインタを渡した場合、typeid の方法は機能しないことに注意してくださいtypeid(*it)typeid(Animal*)そのように使用すると、役に立たないものだけが得られます。

同様に、dynamic_cast使用できます:

for(std::vector<Animal*>::iterator it = v.begin(); it != v.end(); ++it) {
    if(Dog * dog = dynamic_cast<Dog*>(*it)) {
        // it's a dog (or inherited from it). use the pointer
    } else if(Cat * cat = dynamic_cast<Cat*>(*it)) {
        // it's a cat (or inherited from it). use the pointer. 
    }
}

どちらの場合も、アニマル タイプはポリモーフである必要があることに注意してください。つまり、少なくとも 1 つの仮想機能を持っているか継承している必要があります。

于 2008-12-02T04:40:06.523 に答える
2

dynamic_castベクトルにアニマル ポインタが含まれている限り、 を使用できます。

vector <Animal *> stuff;

for(int i=0;i<stuff.size();i++) {
    Dog *pDog = dynamic_cast <Dog *> (stuff[i]);
    if(pDog) {
        // do whatever with the dog
    }

    Cat *pCat = dynamic_cast <Cat *> (stuff[i]);
    if(pCat) {
        // and so on
    }
}

ただし、これは一般的にベスト プラクティスではないことに注意してください。ポリモーフィズムに反対するのではなく、ポリモーフィズムに取り組むようにしてください。つまり、オーバーライドする仮想Animal関数を作成して、コンパイラが自動的に適切な関数を呼び出せるようにします。DogCat

(また、dynamic_cast比較的遅いため、それらの数が多すぎるとパフォーマンスが低下します。一方、仮想関数呼び出しは一般に 1 つの命令にすぎません。)

于 2008-12-02T04:13:09.683 に答える
1

犬と非犬を識別するためにアプリケーションレベルが本当に必要な場合は、RTTI(dynamic_castおよびtypeid)の使用を避け、その知識をクラス階層で明示的にする必要があります。

for (size_t i = 0; i != v.size(); ++i) {
    if (v[i]->isDog()) { v->cleanupPoop(); }
}

パフォーマンス上のわずかな利点がいくつかありますが、主な利点は、クラスインターフェイスで必要な動作をメンテナンスプログラマーに公開することです。クラス階層が機能するために、RTTI(制限されている)は必要ありません。

さて、他の人が言ったことと同様に、isDog()関数は、階層全体の知識を事前に必要としないもの(などneedsPoopCleanup())にリファクタリングされる可能性があります。他の誰もが言ったように、アプリケーションロジックがオブジェクトタイプに基づいて条件付きで実行されると、ポリモーフィズムの利点が失われます。

于 2008-12-02T04:59:04.927 に答える
1

よろしいですか?あなたがやろうとしていることは、ポリモーフィズムの正反対であり、ポリモーフィズムはオブジェクト指向プログラミングにおいて最良のものです。

大まかに言えば、動物が犬の場合は何もしないでください。オブジェクトの 1 つが犬の場合、動物階層に何をすべきかを知らせてください。:)

于 2008-12-02T04:14:49.460 に答える
0

仮想関数の使用:

他の回答で示されているように、仮想関数の使用は実際には十分であることが多く、「C++」の考え方です。仮想関数の使用例を次に示します。

#include<iostream>
#include<vector>
using namespace std;

/////////////

class Animal {
  public:
    virtual void move() { cout << "animal just moved" << endl; }
};
class Dog : public Animal {
  public:
    void move() { cout << "dog just moved" << endl; }
};
class Cat : public Animal {
  public:
    void move() { cout << "cat just moved" << endl; }
};

void doSomethingWithAnimal(Animal *a) {
  a->move();
}

/////////////

int main() {
  vector<Animal*> vec;
  vector<Animal*>::iterator it;

  Animal *a = new Animal;
  Dog *d = new Dog;
  Cat *c = new Cat;

  vec.push_back(a);
  vec.push_back(d);
  vec.push_back(c);

  it = vec.begin();

  while( it != vec.end() ) {
    doSomethingWithAnimal(*it);

    it++;
  }

  return 0;
}

これが十分でない場合は、他の人がすでに、重合論理の代わりに条件付き論理を実際に使用する回答を投稿しています。

于 2008-12-02T05:14:23.460 に答える
0

受け入れられた答えは正しいですが、言及されていない別のオプションもあることを知っておく必要があります。「type()」と呼ばれるAnimalクラスに、intまたはstring(または同等の任意の型)を返すことができる仮想関数を含めることができます。

したがって、たとえば:

class Animal {
    /*...*/
public:
    virtual std::string type() const { return "animal"; }
};

class Dog: public Animal{
    /*...*/
public:
    virtual std::string type() const { return "dog"; }
};

class Cat: public Animal{
    /*...*/
public:
    virtual std::string type() const { return "cat"; }
};

このようにして、次のことができます。

if(array[i]->type() == "dog") { }

type関数は何でも返すことができます(各派生型に固有のintも機能しますが、文字列の方がわかりやすくなります)。

単に別のオプション。

于 2008-12-02T05:15:49.573 に答える
0

typeid演算子を使用してこれを行うことができます。

if (typeid(stuff[i].getClass())==typeid(Dog))

ただし、これが の派生クラスである場合はキャッチできませんDog。そのためにa を使用できますdynamic_cast。ただし、 または の使用は、typeid多くdynamic_castの場合、設計上の欠陥を示しています。通常、派生型が何であるかを知る必要はありません。おそらく、ポリモーフィズムを含むより良い方法があります。ただし、実際の例がなければ適切なアドバイスを与えることは困難です。

于 2008-12-02T04:16:18.523 に答える