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 (指针)或抛异常(引用 |
类型要求 | 无需多态(类无需虚函数) | 必须多态(基类至少包含一个虚函数) |