以下面的C ++为例。
vector<Animal> listAnimal;
class Fish : Animal ...
class Mammal : Animal ...
class Bird : Animal ...
如果我然后将它们全部添加到列表中,然后将它们从列表中任意抓取,我就不知道我正在处理哪个子类。我在Java中可以getClass()
或thefish instanceof Fish
。我如何在C ++中执行此操作?
答案 0 :(得分:9)
您不应该知道您正在处理的子类的类型。如果你需要检查你正在处理的类的类型,你就不会正确地执行多态。多态性的全部意义在于减少if并使代码更加灵活。
但是在某些情况下您需要知道并且可以使用RTTI,但我建议不要这样做,特别是如果您需要很多性能(例如游戏或图形程序)。
使用typeid
运算符获取有关类的信息,并确定某个类是否为特定类型。
例如:
Animal* animal1 = new Cat;
if(typeid(animal1) == typeid(Cat))
{
cout << "animal1 is a: " << typeid(Cat).name();
}
然后使用static_cast
将其抛向hiearchy。
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类中,特定于所有Cats的方法称为:scratchTheLivingHellOutOfYou()
。我们还要说:我有一个Animal *的列表,我想为列表中的每个Cat调用scratchTheLivingHellOutOfYou()
(这包括派生自Cat类的类)。如果使用typeid
运算符和static_cast
,则无法实现所需。由于typeid
仅检查当前类型而不关心层次结构。为此,您必须使用dynamic_cast
,因为它将检查类是否从基类派生,然后相应地向上/向下抛出层次结构。
你可以在C ++中看到这个简单的例子here。以下是该计划的输出:
USING TYPEID
*scratch*
meoyawnn!
RAWR
USING DYNAMIC_CAST
*scratch*
meoyawnn!
*scratch*
RAWR
因此,您可以清楚地看到dynamic_cast
比简单的typeid
和static_cast
做了更多的工作。由于dynamic_cast
查找了层次结构以查看是否是特定类型。简单地说...... dynamic_cast
可以在层次结构中强制和。而typeid
和static_cast
只能将层次结构转换为特定类型。
我想提一下,如果dynamic_cast
失败,它将返回一个NULL指针,或者如果你将它与引用一起使用则抛出异常。
dynamic_cast
。如果你作为一名程序员,知道你所投的任何内容是100%将成为那种类型,那么使用static_cast
,例如如果你知道animal1 正在成为Cat
,那么static_cast
更合适。 答案 1 :(得分: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;
};
答案 2 :(得分:-1)
我通常会创建一个纯虚函数,每个派生类都会实现它来告诉你它的身份。例如:
enum AnimalType
{
Fish = 0,
Mammal,
Bird
}
class Animal
{
virtual AnimalType GetType() const = 0;
}
...
AnimalType Bird::GetType()
{
return Bird;
}
然后你可以这样做:
if (animal.GetType() == Bird)
{
// ...
}