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();
}

然后使用 astatic_cast将其向下转换层次结构。

Animal* animal1 = new Cat;

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

或者,您可以使用dynamic_cast比 typeid/static_cast 更安全但慢得多的 a。像这样:

Animal* animal1 = new Cat;

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

编辑:

dynamic_cast速度较慢仅仅是因为它必须做一些额外的工作,而不仅仅是测试它是否是特定类型和强制转换。iedyanmic_cast不等于typeid/static_cast,但几乎是。

想象一个深度超过 2 层的层次结构,例如:

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

假设在 Cat 类中,调用了特定于所有 Cat 的方法:scratchTheLivingHellOutOfYou()。还可以这样说:我有一个 Animals 列表,我想调用scratchTheLivingHellOutOfYou()列表中的每个 Cat(这包括派生自 Cat 类的类)。如果使用typeidand 运算符static_cast,这将无法实现所需的功能,因为typeid只检查当前类型而不关心层次结构。为此,您必须使用 a dynamic_cast,因为它会检查一个类是否派生自基类,然后相应地向上/向下转换层次结构。

您可以在此处查看这个简单的 C++ 示例。这是程序的输出:

USING TYPEID


*scratch*
meoyawnn!
RAWR



USING DYNAMIC_CAST


*scratch*
meoyawnn!
*scratch*
RAWR

因此,您可以清楚地看到,它比简单的anddynamic_cast做了更多的工作。由于查找层次结构以查看它是否是特定类型。简单地说......可以上下层级。而 a and只能将层次结构向下转换为特定类型。typeidstatic_castdynamic_castdynamic_casttypeidstatic_cast

我想我会提到,如果dynamic_cast失败,它将返回一个 NULL 指针,或者如果您将它与引用一起使用,则抛出异常。

笔记:

  1. 如果您确实需要性能并且需要检查多态对象的类型,我建议您找到 RTTI 的替代方法,例如使用模板/宏或其他任何方法来识别类。
  2. dynamic_cast仅当您不确定对象是否是您要转换的类型时才应使用。如果你,作为一个程序员,知道你正在铸造的任何东西都是 100% 将是那种类型,那么使用static_cast,例如,如果你知道 animal1是 aCat那么static_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

我通常创建一个纯虚函数,每个派生类都实现它来告诉你它的身份。例子:

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 回答