一、什么是RTTI机制
C++ 是一种静态类型语言。其数据类型是在编译期就确定的,不能在运行时更改。然而由于面向对象程序设计中多态性的要求,C++ 中的指针或引用(Reference)本身的类型,可能与它实际代表(指向或引用)的类型并不一致。有时我们需要将一个多态指针转换为其实际指向对象的类型,就需要知道运行时的类型信息,这就产生了运行时类型识别的要求。
RTTI(Run-Time Type Information,运行时类型信息)是 C++ 中的一种机制,它允许程序在运行时确定对象的类型。RTTI 主要通过两个操作符实现:typeid
和 dynamic_cast
。
typeid
操作符:typeid
操作符用于返回表达式的类型信息。它返回一个std::type_info
对象的引用,该对象包含有关类型的名称和其他信息。typeid
可以用于任何类型的表达式,包括类类型和内置类型。dynamic_cast
操作符 :dynamic_cast
是一个用于处理对象指针或引用的安全向下转型操作符。它允许在运行时检查类型兼容性,并将指向基类的指针或引用安全地转换为指向派生类的指针或引用。
需要注意的是,RTTI 机制会增加程序的运行时开销,因为它需要在运行时维护类型信息。此外,RTTI 可能会导致设计上的问题,因为它鼓励依赖于类型而不是接口的编程风格。因此,在使用 RTTI 时应该谨慎,并确保它确实是你解决问题的最佳方式。
二、typeid操作符的使用
在 C++ 中,typeid
是一个操作符,它用于获取表达式的类型信息。typeid
操作符返回一个 std::type_info
类型的对象,该对象包含了关于表达式的类型的详细信息,如类型的名字。typeid
可以用于任何类型的表达式,包括类类型和内置类型。
class Animal
{
private:string name;public:Animal(void);Animal(string name);virtual void sound(void) = 0;string getName(void);void setName(string name);
};Animal::Animal(void) : name("") {}
Animal::Animal(string name) : name(name) {}string Animal::getName(void)
{return name;
}void Animal::setName(string name)
{this->name = name;
}
class Cat : public Animal
{
public:// 使用父类的构造函数using Animal::Animal;void sound(void) override;
};void Cat::sound(void)
{cout << getName() << "在叫:喵喵喵" << endl;
}
class Dog : public Animal
{
public:// 使用父类的构造函数using Animal::Animal;void sound(void) override;
};void Dog::sound(void)
{cout << getName() << "在叫:汪汪汪" << endl;
}
int main(void)
{Animal *cat = new Cat("汤姆");Animal *dog = new Dog("斯派克");Animal *animal = cat;cout << typeid(animal).name() << endl;cout << typeid(*animal).name() << endl;animal = dog;cout << typeid(animal).name() << endl;cout << typeid(*animal).name() << endl;delete cat;delete dog;return 0;
}
在这个例子中,typeid(animal)
返回的是指针 animal
的类型,而 typeid(*animal)
返回的是 animal
指向的对象的类型。由于 Animal
类有一个虚析构函数,typeid(*animal)
能够在运行时确定对象的实际类型。
需要注意的是,typeid
对于具有多态性质的类(即具有虚函数的类)能够提供更准确的类型信息。对于没有虚函数的类,typeid
可能只能提供基类的类型信息,而不是实际对象的类型。此外,typeid
的性能开销应该考虑,因为它涉及到运行时类型检查。
三、dynamic_cast操作符的使用
在 C++ 中,dynamic_cast
是一个操作符,用于在运行时将对象指针或引用安全地转换为目标类型的指针或引用。这种转换通常用于处理继承和多态的情况,尤其是在需要向下转型(从基类指针或引用转型为派生类指针或引用)时。
dynamic_cast
的主要特点是可以检查转换的合法性,如果转换是不合法的(例如,将一个基类指针转换为一个与之不相关的派生类指针),dynamic_cast
将返回 nullptr
(对于指针类型)或者抛出一个 std::bad_cast
异常(对于引用类型)。
使用 dynamic_cast
需要满足以下条件:
- 基类必须至少有一个虚函数,这样编译器才会为基类生成类型信息。
- 转换的目标类型必须是类的指针或引用。
在 Cat 类中新增一个捉老鼠的方法。
class Cat : public Animal
{
public:// 使用父类的构造函数using Animal::Animal;void sound(void) override;void catchMouse(void);
};void Cat::sound(void)
{cout << getName() << "在叫:喵喵喵" << endl;
}void Cat::catchMouse(void)
{cout << getName() << "在抓老鼠" << endl;
}
int main(void)
{Animal *animal = new Cat("汤姆");Cat *cat = dynamic_cast<Cat *>(animal);if (cat != nullptr){cat->catchMouse();}delete animal;return 0;
}
在这个例子中,我们创建了一个 Cat
类型的对象,并通过基类指针 animal
来引用它。使用 dynamic_cast
将 animal
转换为 Cat*
类型的指针 cat
。由于 animal
实际上指向的是一个 Cat
对象,所以转换成功,并且我们可以通过 cat
调用 Cat
类的特有的成员函数。
如果尝试将一个基类指针转换为不相关的派生类指针,dynamic_cast
将失败,返回 nullptr
: