文章目录
- 1.RTTI
- 2.dynamic_cast运算符
- 3.typeid运算符
- 4.RTTI与虚函数表
1.RTTI
RTTI(Run Time Type Identification),即运行时类型识别,通过 RTTI,程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。
RTTI 提供了两个非常有用的运算符:
-
dynamic_cast 运算符:将基类类型的指针或引用安全地转换为其派生类类型的指针或引用。
-
typeid 运算符:返回指针或引用所指对象的实际类型。
要想让 RTTI 的两个运算符能够正常工作,那么基类中必须至少要有一个虚函数,不然这两个运算符工作的结果可能跟预期结果不一样,因为只有虚函数的存在,这两个运算符才会使用指针或引用所绑定的对象的动态类型。
2.dynamic_cast运算符
如果 dynamic_cast 运算符能够转换成功,说明这个指针实际上就是要转换到的那个类型,也就是说 dynamic_cast 运算符能够做运行时安全检查。
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void run() { cout << "Animal::run()" << endl; }
};
class Dog : public Animal
{
public:
void run() { cout << "Dog::run()" << endl; }
void speak() { cout << "Dog::speak()" << endl; }
};
int main()
{
Animal* pa = new Dog();
//pa->speak(); // 报错
Dog* pd = dynamic_cast<Dog*>(pa);
if (pd != nullptr)
{
cout << "转换成功" << endl;
pd->speak(); // 正确
}
else
{
cout << "转换失败" << endl;
}
return 0;
}
对于引用,如果用 dynamic_cast 转换失败,则系统会抛出一个 std::bad_cast 异常,如下代码所示。
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void run() { cout << "Animal::run()" << endl; }
};
class Dog : public Animal
{
public:
void run() { cout << "Dog::run()" << endl; }
void speak() { cout << "Dog::speak()" << endl; }
};
int main()
{
Animal* pa = new Dog();
Animal& refa = *pa;
//refa.speak(); // 报错
try
{
Dog& refd = dynamic_cast<Dog&>(refa); // 如果转换不成功,则流程直接进入到catch里边去;如果转换成功,则流程继续往下走
cout << "转换成功" << endl;
refd.speak(); // 正确
}
catch (std::bad_cast)
{
cout << "转换失败" << endl;
}
return 0;
}
3.typeid运算符
typeid() 的主要作用就是让用户知道当前的变量是什么类型的。
typeid() 返回的是一个 type_info 类型的常量对象的引用,即 const type_info&
。type_info 类中的成员函数 name() 返回的是一个C语言风格的字符串。
#include <iostream>
using namespace std;
int main()
{
int a = 180;
cout << typeid(a).name() << endl;
char b[] = "hello";
cout << typeid(b).name() << endl;
cout << typeid(19.6).name() << endl;
cout << typeid("world").name() << endl;
return 0;
}
输出结果如下:
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void run() { cout << "Animal::run()" << endl; }
};
class Dog : public Animal
{
public:
void run() { cout << "Dog::run()" << endl; }
};
class Cat : public Animal
{
public:
void run() { cout << "Cat::run()" << endl; }
};
int main()
{
Animal* p1 = new Dog();
cout << typeid(p1).name() << endl;
cout << typeid(*p1).name() << endl;
if (typeid(*p1) == typeid(Dog))
{
cout << "将Dog类型的对象动态绑定到p1指针上" << endl;
}
else
{
cout << "没有将Dog类型的对象动态绑定到p1指针上" << endl;
}
Animal* p2 = new Cat();
cout << typeid(p2).name() << endl;
cout << typeid(*p2).name() << endl;
if (typeid(*p2) == typeid(Cat))
{
cout << "将Cat类型的对象动态绑定到p2指针上" << endl;
}
else
{
cout << "没有将Cat类型的对象动态绑定到p2指针上" << endl;
}
return 0;
}
输出结果如下:
如下代码所示,如果基类中不含有虚函数,则 typeid() 返回的是表达式的静态类型(即定义的类型),既然是定义的类型,那么编译器不需要对表达式求值也能知道表达式的静态类型。
#include <iostream>
using namespace std;
class Animal
{
public:
void run() { cout << "Animal::run()" << endl; }
};
class Dog : public Animal
{
public:
void run() { cout << "Dog::run()" << endl; }
};
class Cat : public Animal
{
public:
void run() { cout << "Cat::run()" << endl; }
};
int main()
{
Animal* p1 = new Dog();
cout << typeid(p1).name() << endl;
cout << typeid(*p1).name() << endl;
if (typeid(*p1) == typeid(Dog))
{
cout << "将Dog类型的对象动态绑定到p1指针上" << endl;
}
else
{
cout << "没有将Dog类型的对象动态绑定到p1指针上" << endl;
}
Animal* p2 = new Cat();
cout << typeid(p2).name() << endl;
cout << typeid(*p2).name() << endl;
if (typeid(*p2) == typeid(Cat))
{
cout << "将Cat类型的对象动态绑定到p2指针上" << endl;
}
else
{
cout << "没有将Cat类型的对象动态绑定到p2指针上" << endl;
}
return 0;
}
输出结果如下:
4.RTTI与虚函数表
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void run() { cout << "Animal::run()" << endl; }
};
class Dog : public Animal
{
public:
void run() { cout << "Dog::run()" << endl; }
};
int main()
{
Animal* pa = new Dog();
const type_info& tf = typeid(*pa);
cout << tf.name() << endl; // class Dog
return 0;
}
在 C++ 中,如果类中含有虚函数,那么编译器就会对该类产生一个虚函数表。
在上面代码中,pa 指向一个 Dog 类型的对象,该对象里有一个指针,称为虚函数表指针,虚函数表指针指向的是该对象所在的类 Dog 的虚函数表。
虚函数表里有很多项,每一项都是一个指针,每个指针指向的是这个类中的各个虚函数的入口地址。
虚函数表中的第一个表项很特殊,它指向的不是虚函数的入口地址,它指向的实际上是这个类所关联的 type_info 对象。