以以下C++代码为例。
vector<Animal> listAnimal;
class Fish : Animal ...
class Mammal : Animal ...
class Bird : Animal ...
如果我将它们全部添加到列表中,然后任意从列表中获取它们,我就不知道我正在处理哪个子类。在Java中,我可以使用getClass()
或thefish instanceof Fish
。在C++中该怎么做?
以以下C++代码为例。
vector<Animal> listAnimal;
class Fish : Animal ...
class Mammal : Animal ...
class Bird : Animal ...
如果我将它们全部添加到列表中,然后任意从列表中获取它们,我就不知道我正在处理哪个子类。在Java中,我可以使用getClass()
或thefish instanceof Fish
。在C++中该怎么做?
如果你需要知道正在处理的子类类型,那么就不是正确的多态。多态的全部意义在于减少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类中,有一个特定于所有猫的方法被调用:scratchTheLivingHellOutOfYou()
。还假设:我有一个动物列表,并且我想为列表中的每只猫调用scratchTheLivingHellOutOfYou()
(这包括从Cat类派生的类)。如果使用typeid
运算符和static_cast
,那么这将无法实现所需的功能,因为typeid
只检查当前类型,不关心层次结构。为此,必须使用dynamic_cast
,因为它将检查一个类是否派生自基类,然后相应地向上/向下转换层次结构。
您可以在C ++中查看此简单示例,此处。以下是程序的输出:
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
失败,它将返回一个空指针,或者如果您在引用中使用它,则会抛出异常。dynamic_cast
。如果您作为程序员知道任何您要转换的内容100%将成为该类型,则使用static_cast
,例如,如果您知道animal1将是Cat,那么static_cast
更合适。dynamic_cast<Cat*>(animal1)
中的*
。 - ascheplerstatic_cast
并且它的效果与 dynamic_cast
完全相同? - Alex Botevstatic_cast
。 - miguel.martin一个容器只能存储固定类型的元素,如果你想要一个指向对象的指针,请使用对象指针。
#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 );
将Animal
类型存储在向量中,当将派生类型推入listAnimal
时会导致对象切片
。
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;
};
我通常会创建一个纯虚函数,每个派生类都实现它来告诉你它的身份。例如:
enum AnimalType
{
Fish = 0,
Mammal,
Bird
}
class Animal
{
virtual AnimalType GetType() const = 0;
}
...
AnimalType Bird::GetType()
{
return Bird;
}
然后你可以像这样做:
if (animal.GetType() == Bird)
{
// ...
}