c++语言核心技术要点,《运行时类型识别RTTI》

本文涉及的产品
实时数仓Hologres,5000CU*H 100GB 3个月
智能开放搜索 OpenSearch行业算法版,1GB 20LCU 1个月
实时计算 Flink 版,5000CU*H 3个月
简介: c++语言核心技术要点,《运行时类型识别RTTI》

1、什么是RTTI?

RTTI:Run-Time Type Identification运行时类型识别

程序在运行时能够使用基类指针或引用来检查这些指针或引用所指的对象的实际的派生类。

在这里要用到两个C++中的关键字:typeiddynamic_cast

2、typeid

typeid是C++中一个用来获取变量类型的关键字,用法跟使用函数一样 typeid(xxx)

typeid注意事项:

(1)typeid返回的是一个type_info对象的引用,而这个引用是与具体的变量类型有关的,

也就是说不同的变量类型都有一个type_info结构体。type_info结构体如下:

class type_info{
public:
     const char* name() const;        //  打印具体类型的方法  一般这样用 typeid(x1).name()
     bool operator==(const type_info& rhs) const;  // 运算符重载  支持直接 typeid(x1) == typeid(x2)
     bool operator!=(const type_info& rhs) const;   // 运算符重载
     int before(const type_info& rhs) const;
     virtual ~type_info();        // 析构函数
private:
     ......
};

(2)xxx可以是一个变量名(typeid(a)),也可以是一个具体的变量类型(typeid(int)),通常是用来做比较的时候,如下例子所示:

int  i  =  0;
cout << typeid(i).name() << endl;    // int型变量 i
cout << typeid(&i).name() << endl;   // int *变量 &i
if (typeid(i) == typeid(int)) {  }

(3)如果我们想通过基类的指针获得派生类的数据类型,基类必须带有虚函数,否则获取的就是基类的类型,并且只能够去获取派生类对象的实际类型,

也就是说我们typeid传参的时候只能是对象类型或者是对象引用,不能是对象指针(注意我这里说的是你想要通过基类获取派生类对象数据类型)

背景:因为我们可以通过基类的指针变量指向一个派生类变量的地址,而typeid关键字提供了使用基类指针去获取派生类对象的数据类型。

 1 #include <iostream>
 2 using namespace std;
 3 
 4 class Fly1{
 5 public:
 6     virtual void fly(void);     //  Fly1基类中含有虚函数
 7 };
 8 
 9 class Fly2{
10 public:
11     void fly(void) ;         // Fly2基类中不含虚函数
12 };
13 
14 class Plane1 : public Fly1   // Plane1派生类继承于Fly1
15 {
16 public:
17     virtual void fly(void);
18     void transport(void);
19 };
20 
21 class Plane2 : public Fly2    // PLane2派生类继承于Fly2
22 {
23 public:
24     virtual void fly(void);
25     void transport(void);
26 };
27 
28 /****************************************/
29 void Fly1::fly(void)
30 {
31      cout << "fly---Fly1" << endl;
32 }
33 
34 void Fly2::fly(void)
35 {
36      cout << "fly---Fly2" << endl;
37 }
38 
39 void Plane1::fly(void)
40 {
41      cout << "fly---Plane1" << endl;
42 }
43 
44 void Plane2::fly(void)
45 {
46      cout << "fly---Plane2" << endl;
47 }
48 
49 int main(void)
50 {
51       Fly1 *fly1 = new Plane1();
52       Fly2 *fly2 = new Plane2();
53 
54       cout << typeid(fly1).name() << endl;
55       cout << typeid(*fly1).name() << endl;
56       cout << typeid(fly2).name() << endl;
57       cout << typeid(*fly2).name() << endl;
58 
59       return 0;
60 }

运行结果如下:

117a073e90e2ea1c9cd5e17ca9888c87_944893-20161028085521187-1635534829.png

因为基类Fly1中含有虚函数,并且fly1是一个指针,所以第一行显示就是基类指针变量的类型,而第二行因为传参就是指针指向的对象(也就是对象实际类型),所以能够获取到派生类对象的类型

因为基类Fly2中不含虚函数,所以不管传入的是对象指针还是对象本身,获取的都是基类对应的类型,而不能获取到派生类对象的类型。

所以说要通过基类指针获取派生类对象的类型:第一条件是基类中含有虚函数  第二条件:只能获取派生类对象的类型,不能获取派生类对象指针的类型。  

 

3、dynamic_cast

dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。

(1)用法:dynamic_cast<type-id>(expression)   type-id就是要转换的类型,expression就是待转换的变量,所以dynamic_cast就是将expression变量转化          为type-id类型的变量,type-id必须是类的指针、类的引用或者是void *。

(2)返回值:对指针进行dynamic_cast,失败返回null,成功返回正常cast后的对象指针;

          对引用进行dynamic_cast,失败抛出一个异常,成功返回正常cast后的对象引用。

(3)多态类型间转换的3种类型: 子类转为父类的向上转换父类转为子类的向下转换平行类间的横向转换(指的是多继承的情况下,各个父类之间的转换)

其中向下转换横行转换要求被转换的类中必须含有虚函数,否则不能转换成功;而向上转换并没有这个要求。

1 #include <iostream>
 2 using namespace std;
 3 
 4 class tao1
 5 {
 6 public:
 7      virtual void a(void) { cout << "a---tao1" << endl; }
 8 };
 9 
10 class tao2
11 {
12 public:
13     virtual void a(void) { cout << "a---tao2" << endl; }
14 };
15 
16 class tao3 : public tao2, public tao1
17 {
18 public:
19     virtual void a(void) { cout << "a---tao3" << endl; }
20 };
21 
22 
23 int main(void)
24 {
25     /*向上转换*/
26 //    tao3 *pTao3 = new tao3();
27 //    tao1 *pTao1 = dynamic_cast<tao1 *>(pTao3);   
28 
29     /*向下转换*/
30 //    tao1 *pTao1 = new tao3();
31 //    tao3 *pTao3 = dynamic_cast<tao3 *>(pTao1);  // 要求被转换的类中函数虚函数
32 
33     /*横向转换*/
34     tao1 *pTao1 = new tao3();
35     tao2 *pTao2 = dynamic_cast<tao2 *>(pTao1);  // 要求被转换的类中含有虚函数
36 
37     return 0;
38 }

代码如下:

4、使用typeid和dynamic_cast构建RTTI

背景:在程序中常常需要这样的操作,当我们的一个函数的参数是一个基类指针的时候,而我们调用这个函数传入的参数是一个派生类指针

     所以我们需要在函数中通过typeid判断我们的派生类对象的类型,然后通过dynamic_cast将我们的基类对象转换为派生类对象,从而可以

     调用派生类中相应的方法进行操作。

代码如下:

1 #include <iostream>
 2 #include <string>
 3 using namespace std;
 4 
 5 class Fly{
 6 public:
 7     virtual void fly(void) = 0;
 8 };
 9 
10 class Plane : public Fly
11 {
12 public:
13     virtual void fly(void);
14     void transport(void);
15 };
16 
17 class Bird : public Fly
18 {
19 public:
20     virtual void fly(void);
21     void eat(void);
22 };
23 
24 /*******************************************/
25 void Plane::fly(void)
26 {
27     cout << "fly---Plane" << endl;
28 }
29 
30 void Plane::transport(void)
31 {
32     cout << "transport---Plane" << endl;
33 }
34 
35 /***************************************************/
36 void Bird::fly(void)
37 {
38     cout << "fly---Bird" << endl;
39 }
40 
41 void Bird::eat(void)
42 {
43     cout << "eat---Bird" << endl;
44 }
45 
46 /*********************************************/
47 static void doSomeThing(Fly *obj)
48 {
49     obj->fly();
50 
51     if (typeid(*obj) == typeid(Plane))   // 判断传进来的指针指向的对象的类型
52     {
53         Plane *ptr = dynamic_cast<Plane *>(obj); // 转换类型
54         ptr->transport();
55     }
56 
57     if (typeid(*obj) == typeid(Bird))
58     {
59         Bird *ptr = dynamic_cast<Bird *>(obj);
60         ptr->eat();
61      }
62 }
63 
64 int main(void)
65 {
66     Plane pla;
67     Bird bir;
68     doSomeThing(&pla);
69     doSomeThing(&bir);
70     return 0;
71 }

运行结果如下:

71c939023474074a70570321f05f25f9_944893-20161028164116984-893235870.png

相关文章
|
3月前
|
C++
C++ 语言异常处理实战:在编程潮流中坚守稳定,开启代码可靠之旅
【8月更文挑战第22天】C++的异常处理机制是确保程序稳定的关键特性。它允许程序在遇到错误时优雅地响应而非直接崩溃。通过`throw`抛出异常,并用`catch`捕获处理,可使程序控制流跳转至错误处理代码。例如,在进行除法运算或文件读取时,若发生除数为零或文件无法打开等错误,则可通过抛出异常并在调用处捕获来妥善处理这些情况。恰当使用异常处理能显著提升程序的健壮性和维护性。
64 2
|
3月前
|
C++
C++ 根据程序运行的时间和cpu频率来计算在另外的cpu上运行所花的时间
C++ 根据程序运行的时间和cpu频率来计算在另外的cpu上运行所花的时间
42 0
|
3月前
|
算法 C语言 C++
C++语言学习指南:从新手到高手,一文带你领略系统编程的巅峰技艺!
【8月更文挑战第22天】C++由Bjarne Stroustrup于1985年创立,凭借卓越性能与灵活性,在系统编程、游戏开发等领域占据重要地位。它继承了C语言的高效性,并引入面向对象编程,使代码更模块化易管理。C++支持基本语法如变量声明与控制结构;通过`iostream`库实现输入输出;利用类与对象实现面向对象编程;提供模板增强代码复用性;具备异常处理机制确保程序健壮性;C++11引入现代化特性简化编程;标准模板库(STL)支持高效编程;多线程支持利用多核优势。虽然学习曲线陡峭,但掌握后可开启高性能编程大门。随着新标准如C++20的发展,C++持续演进,提供更多开发可能性。
71 0
|
15天前
|
算法 C++
2022年第十三届蓝桥杯大赛C/C++语言B组省赛题解
2022年第十三届蓝桥杯大赛C/C++语言B组省赛题解
20 5
|
2月前
|
存储 算法 C++
C++提高篇:泛型编程和STL技术详解,探讨C++更深层的使用
文章详细探讨了C++中的泛型编程与STL技术,重点讲解了如何使用模板来创建通用的函数和类,以及模板在提高代码复用性和灵活性方面的作用。
46 2
C++提高篇:泛型编程和STL技术详解,探讨C++更深层的使用
|
1月前
|
存储 编译器 程序员
C++类型参数化
【10月更文挑战第1天】在 C++ 中,模板是实现类型参数化的主要工具,用于编写能处理多种数据类型的代码。模板分为函数模板和类模板。函数模板以 `template` 关键字定义,允许使用任意类型参数 `T`,并在调用时自动推导具体类型。类模板则定义泛型类,如动态数组,可在实例化时指定具体类型。模板还支持特化,为特定类型提供定制实现。模板在编译时实例化,需放置在头文件中以确保编译器可见。
27 11
|
20天前
|
存储 编译器 C语言
深入计算机语言之C++:类与对象(上)
深入计算机语言之C++:类与对象(上)
|
20天前
|
存储 分布式计算 编译器
深入计算机语言之C++:C到C++的过度-2
深入计算机语言之C++:C到C++的过度-2
|
20天前
|
编译器 Linux C语言
深入计算机语言之C++:C到C++的过度-1
深入计算机语言之C++:C到C++的过度-1
|
2月前
|
JavaScript 前端开发 测试技术
一个google Test文件C++语言案例
这篇文章我们来介绍一下真正的C++语言如何用GTest来实现单元测试。
17 0