运行时类型识别
运行时类型识别
运行时类型识别(RTTI)主要作用是获得指针及引用变量的实际类型, 其主要操作有两种, 分别是 获取实际类型 与 安全类型转换;
获取实际类型
获取实际类型主要依靠: typeid 关键字 与 type_info 类, 示例代码如下:
类代码:
// demo.h
class Demo {
public:
Demo() = default;
virtual ~Demo() = default;
virtual void disp() {cout << "Demo" << endl;}
inline double dispA() {return m_a;}
private:
double m_a = 10.0;
};
class DemoDerived : public Demo {
public:
DemoDerived() = default;
~DemoDerived() = default;
virtual void disp() override {cout << "DemoDeriv" << endl;}
inline double dispB() {return m_b;}
private:
double m_b = 8.0;
};
主函数:
#include <iostream>
#include <typeinfo>
#include "demo.h"
using std::cout;
using std::endl;
using std::type_info;
int main()
{
Demo* demo1 = new Demo();
Demo* demo2 = new DemoDerived();
const type_info& type1 = typeid (*demo1);
const type_info& type2 = typeid (*demo2);
cout << type1.name() << endl;
cout << type2.name() << endl;
delete demo1;
delete demo2;
return 0;
}
终端输出:
// console output :
4Demo
11DemoDerived
不同编译器可能输出略微不同, 但是仍可看到两个指针所指向的实际类型分别为 Demo 与 DemoDerived, 其中数字表示类型名称的字符长度;
安全类型转换
在基类与继承类之间进行安全类型转换主要依靠 dynamic_cast<>() 操作, 示例代码如下;
#include <iostream>
#include <typeinfo>
#include "demo.h"
using std::cout;
using std::endl;
using std::type_info;
int main()
{
Demo* demo1 = new Demo();
Demo* demo2 = new DemoDerived();
DemoDerived* demoCast1 = dynamic_cast<DemoDerived*>(demo1);
DemoDerived* demoCast2 = dynamic_cast<DemoDerived*>(demo2);
cout << "demoCast1 addr = " << demoCast1 << endl;
cout << "demoCast2 addr = " << demoCast2 << endl;
delete demo1;
delete demo2;
return 0;
}
上述代码终端输出:
// console output :
demoCast1 addr = 0
demoCast2 addr = 0x55555556aed0
其中, demo2 的实际类型是 DemoDerived, 所以动态类型转换成功, demoCast2是有效地址;demo1 的实际类型 Demo, 所以动态类型转换失败, demoCast1 地址为 0, 是无效地址, 表示了这种转换是不正确的, 这正是动态类型转换的安全性所在;
而静态类型转换 static_cast 不会检查类型转换之间的安全性, 反而会引起一些潜在危险,且难以排查, 示例代码如下:
#include <iostream>
#include <typeinfo>
#include "demo.h"
using std::cout;
using std::endl;
using std::type_info;
int main()
{
Demo* demo1 = new Demo();
Demo* demo2 = new DemoDerived();
DemoDerived* ptrDemoError = static_cast<DemoDerived *>(demo1);
cout << "ptrDemoError addr = " << ptrDemoError << endl;
cout << ptrDemoError->dispA() << endl;
cout << ptrDemoError->dispB() << endl;
delete demo1;
delete demo2;
return 0;
}
上述代码终端输出为:
// console output :
ptrDemoError addr = 0x55555556aeb0
10
0
虽然上述代码中的类型转换是不正确的, 但是静态类型转换仍然能够获得demo1的地址, 而后其错误地使用了DemoDerived中的dispB(), 给出了错误的结果;这种错误排查难度远远大于程序意外终止等明显的问题;
所以, 在基类与继承类之间的转换中尽量只使用 dynamic_cast, 不要使用C风格的强制类型转换 与 static_cast 等不安全的类型转换, 除非自己明确知道这种类型的转换一定是正确的, 否则产生错误可能非常难以排查;
总结
运行时类型识别RTTI虽然会降低程序性能, 但是适当使用提升了程序的安全性, 对编码起到事半功倍的效果;