C++运行时类型识别
C++运行时类型识别
简介
运行时类型识别是C++提供的一种机制,允许程序在运行时获取对象的实际类型信息,而非仅依赖编译时的静态类型。这一特性主要解决多态场景下,基类指针/引用指向派生类对象时,无法直接确定其真实类型的问题(如Base * b = new Derived;
中,b的声明类型是Base,但实际指向Derived
对象
typeid
的介绍
typeid
是C++的运行时类型识别的关键字,用于获取对象或表达式的类型信息,返回一个const std::type_info&
对象,它主要用于多态场景中确定对象的实际类型
-
基本语法:
typeid
(表达式)或typeid
(类型名),在使用时需要调用头文件<typeinfo> -
返回值:
const std::type_info&
,在不同编译环境下的输出内容可能会有所不同
typeid
的用法多样,在visual stdio
中可以根据编译器建议发现typeid
的多种用法形式
在这里我们仅介绍使用频率最高的用法
#include <typeinfo> #include <iostream> using namespace std; int main() { int a = 19; //不同编译器输出结果可能不同 cout << typeid(a).name() << endl; cout << typeid(int).name() << endl; }
visual studio code
输出结果
visual studio
输出结果
可以看出在不同编译环境下的输出格式并不相同,后续为了能够直观的感受程序输出因此都展示visual studio
的输出结果
核心作用
-
识别多态对象的实际类型:对于基类指针/引用指向派生类对象的场景,
typeid
会返回的类型(需要确保基类又虚函数) -
比较类型是否相同:
type_info
重载了操作符==
,!=
通过==
或!=
比较两个type_info
对象是否相等,不等,函数name()
返回类型值名称
#include <typeinfo> #include <iostream> using namespace std; //基类 class Base { virtual void a() { } }; class Derived : public Base { }; int main() { Base* b = new Derived(); cout << typeid(*b).name() << endl; delete b; return 0; }
输出结果
结合上述一系列操作,我们还可以结合指针实现一些复杂操作
#include <typeinfo> #include <iostream> using namespace std; class A { public: virtual void func() { cout << "A" << endl; } }; class B : public A { public: void func() { cout << "B" << endl; } }; void test(A* pa) { //通过typeid做类型检查 if (typeid(*pa).name() == typeid(B).name()) { pa->func(); } } int main() { B * pb; pa = &b; pa->func(); //传入对象为B的实例化成员 test(pa); //输出指针的类型 cout << typeid(pa).name() << endl; //输出指针指向的对象类型 cout << typeid(*pa).name() << endl; return 0; }
输出结果
上述代码中我们实现了通过指针来输出指针类型和指针指向类型,也实现了通过类成员的类型作为条件判断调用不同类内的函数
转型操作
向上转型操作在之前就提到过,这也是C++中的一大特征呢,允许派生类直接给基类赋值,我们在操作时其实也就相当于在隐式调用static_cast
关键字
static_cast
关键字介绍
基本语法
//基本数据类型转换 目标类型 变量 = static_cast<目标类型>(源表达式); //向上转型 基类指针/引用 = static_cast<基类类型*>(派生类指针引用); //向下转型 派生类指针/引用 = static_cast<派生类类型*>(基类指针/引用);
static_cast
是C++中最常用的静态类型转换关键字,用于在编译期完成类型转换,适用于多种场景
在这里我们仅介绍与C++转型操作有关的使用场景
向上转型
class Base { ......; }; class Derived : public Base {}; Derived d; //派生类->基类 Base * pb = static_cast<Base*>(&d);
当然static_cast
关键字还能实现向下转型操作,但是我们并不提倡这种做法,因为static_cast
关键字并不能提供类型检查,所以使用static_cast
实现向下转型是一种很危险的操作
#include <typeinfo> #include <iostream> using namespace std; class A { public: virtual void func() { cout << "A" << endl; } }; class B : public A { public: void func() { cout << "B" << endl; } }; int main() { A a; B b; A * pa = &a; B * pb = &b; //向上转型操作 a = static_cast<A>(b); pa = static_cast<A*>(pb); //向下转型操作 //引发报错:基类对象不能直接赋值给派生类对象 b = a; //引发报错:基类对象不能直接赋值给派生类指针 pb = pa; //引发报错:不能直接将类对象转化为派生类对象 b = static_cast<B>(a); //不会引发报错,但是存在潜在危险,不能忽视 pb = static_cast<B*>(pa); return 0; }
dynamic_cast
关键字介绍
操作限制
dynamic_cast
关键字就能够实现向下转型的操作,但是在操作时也会受到限制
-
dynamic_cast
仅支持两种转换-
基类指针->派生类指针
-
基类引用->派生类引用
-
-
仅适用于多态类型,即基类必须要包含虚函数
对于上一部分代码中的第45行语句,只要将其中的static_cast
关键字转换为dynamic_cast
即可完成转换
语法形式:
//基础语法模板 dynamic_cast<目标类型>(源对象) //指针类型转换 目标类型指针 = dynamic_cast<目标类型*>(源指针); //引用类型转换 目标类型引用 = dynamic_cast<目标类型&>(源引用);
失败返回值:
-
若对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针
-
若对引用进行dynamic_cast,失败抛出一个异常,成功返回正常cast后的对象引用
类型检查
void test(A * pa) { //判断传入对象是否为B类型 B *pb = dynamic_cast<B *>(pa); //如果不等于空指针,则表明转换成功 if(pb != nullptr) { pb->func(); } }
对比
特性 | static_cast | dynamic_cast |
---|---|---|
检查时机 | 编译期(无运行时检查) | 运行时(通过RTTI 机制检查类型兼容性) |
向下转型风险 | 可能产生不安全的转换(如基类指针转派生类) | 若转换不安全,返回nullptr (指针)或抛异常(引用 |
类型要求 | 无需多态(类无需虚函数) | 必须多态(基类至少包含一个虚函数) |
更多推荐
所有评论(0)