11

たとえば、次のC++を取り上げます。

vector<Animal> listAnimal;

class Fish : Animal ...
class Mammal : Animal ...
class Bird : Animal ...

次に、それらすべてをリストに追加し、リストから任意に取得すると、どのサブクラスを扱っているのかわかりません。私はJavaでできるgetClass()thefish instanceof Fish。C ++でこれを行うにはどうすればよいですか?

4

3 に答える 3

13

扱っているサブクラスのタイプを知る必要はありません。扱っているクラスのタイプをチェックする必要がある場合は、ポリモーフィズムを正しく実行していません。ポリモーフィズムの要点は、ifを減らし、コードをより柔軟にすることです。

知っておく必要がある場合があり、そのためにRTTIを使用できます。ただし、特に多くのパフォーマンスが必要な場合(ゲームやグラフィックプログラムなど)は、そうしないことをお勧めします。

演算子を使用してtypeid、クラスに関する情報を取得し、クラスが特定のタイプであるかどうかを判別します。

例えば:

Animal* animal1 = new Cat;

if(typeid(animal1) == typeid(Cat))
{
     cout << "animal1 is a: " << typeid(Cat).name();
}

次に、を使用static_castして階層の下にキャストします。

Animal* animal1 = new Cat;

if(typeid(animal1) == typeid(Cat))
{
     Cat* cat = static_cast<Cat*>(animal1);
     cat->scratchTheLivingHellOutOfYou();
}

dynamic_castまたは、typeid / static_castよりもはるかに安全ですが、はるかに遅いを使用できます。そのようです:

Animal* animal1 = new Cat;

if(Cat* cat = dynamic_cast<Cat*>(animal1)
{
     cat->scratchTheLivingHellOutOfYou();
}

編集:

dynamic_castそれが特定のタイプであるかどうかをテストしてキャストするよりも少し余分な作業をしなければならないという理由だけで遅くなります。つまり、dyanmic_castと同等ではありませんtypeid/static_castが、ほとんど同等です。

たとえば、2レベルを超える深さの階層を想像してみてください。

class Animal { /* ... */ }; // Base
class Cat : public Animal { /* ... */ }; // 2nd level
class Tiger : public Cat { /* ... */ }; // 3rd level

Catクラスで、すべてのCatに固有のメソッドが呼び出されたとしますscratchTheLivingHellOutOfYou()。また、次のように言いましょう。動物のリストがあり、リストscratchTheLivingHellOutOfYou()内のすべての猫を呼び出したいと思います(これには、クラスCatから派生したクラスが含まれます)。typeid演算子とが使用されている場合、現在のタイプをチェックするだけで階層を気にしないstatic_castため、これは必要なことを達成しません。typeidこのためにはdynamic_cast、クラスが基本クラスから派生しているかどうかをチェックし、それに応じて階層を上下にキャストするため、を使用する必要があります。

この簡単な例は、C++でここに表示されます。プログラムの出力は次のとおりです。

USING TYPEID


*scratch*
meoyawnn!
RAWR



USING DYNAMIC_CAST


*scratch*
meoyawnn!
*scratch*
RAWR

したがって、これは単純なやdynamic_castよりもはるかに多くの作業を行うことがはっきりとわかります。階層を検索して、それが特定のタイプであるかどうかを確認します。簡単に言えば...階層上下にキャストできます。一方、とは階層を特定のタイプにキャストすることしかできません。typeidstatic_castdynamic_castdynamic_casttypeidstatic_cast

dynamic_cast失敗した場合はNULLポインターを返すか、参照で使用している場合は例外をスローすることをお伝えしたいと思います。

ノート:

  1. 本当にパフォーマンスが必要で、ポリモーフィックオブジェクトのタイプをチェックする必要がある場合は、テンプレート/マクロなどを使用してクラスを識別するなど、RTTIに代わるものを見つけることをお勧めします。
  2. dynamic_castオブジェクトが変換先のタイプであるかどうかわからない場合にのみ使用してください。プログラマーとして、キャストするものが100%そのタイプになることstatic_castを知っている場合は、を使用します。たとえば、animal1がthenになることがわかっている場合はより適切です。Catstatic_cast
于 2013-01-25T23:37:08.543 に答える
2

コンテナは固定タイプの要素のみを格納します。オブジェクトへのポインタが必要です。

#include <memory>
#include <vector>

std::vector<std::unique_ptr<Animal>> animal_list;

animal_list.emplace_back(new Fish);
animal_list.emplace_back(new Mammal);
animal_list.emplace_back(new Bird );

派生型をlistAnimalにプッシュするAnimalと、型をベクトルに格納します。object slice

 vector<Animal> listAnimal;
 listAnimal.push_back(Fish);  // Fish is sliced to Animal, no fish for you.

編集:

派生動物の種類を知るために、メンバーに保存することができます

Enum AnimalType
{
  FISH,
  MAMAL,
  BIRD
};

class Animal
{
public:
  Animal(AnimalType animal_type) : type(animal_type) {}
  AnimalType GetType() const { return type; }
private:
  AnimalType type;   
};
于 2013-01-25T23:32:04.220 に答える
-1

私は通常、各派生クラスがそのIDを通知するために実装する純粋仮想関数を作成します。例:

enum AnimalType
{
   Fish = 0,
   Mammal,
   Bird
}

class Animal
{
   virtual AnimalType GetType() const = 0;
}

...

AnimalType Bird::GetType()
{
   return Bird;
}

次に、次のようなことを行うことができます。

if (animal.GetType() == Bird)
{
   // ...
}
于 2013-01-25T23:47:53.120 に答える