• 【深入理解C++】RTTI、dynamic_cast、typeid()


    1.RTTI

    RTTI(Run Time Type Identification),即运行时类型识别,通过 RTTI,程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。

    RTTI 提供了两个非常有用的运算符

    • dynamic_cast 运算符:将基类类型的指针或引用安全地转换为其派生类类型的指针或引用。

    • typeid 运算符:返回指针或引用所指对象的实际类型。

    要想让 RTTI 的两个运算符能够正常工作,那么基类中必须至少要有一个虚函数,不然这两个运算符工作的结果可能跟预期结果不一样,因为只有虚函数的存在,这两个运算符才会使用指针或引用所绑定的对象的动态类型。

    2.dynamic_cast运算符

    如果 dynamic_cast 运算符能够转换成功,说明这个指针实际上就是要转换到的那个类型,也就是说 dynamic_cast 运算符能够做运行时安全检查。

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    对于引用,如果用 dynamic_cast 转换失败,则系统会抛出一个 std::bad_cast 异常,如下代码所示。

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    3.typeid运算符

    typeid() 的主要作用就是让用户知道当前的变量是什么类型的。

    typeid() 返回的是一个 type_info 类型的常量对象的引用,即 const type_info&。type_info 类中的成员函数 name() 返回的是一个C语言风格的字符串。

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    输出结果如下:

    在这里插入图片描述

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    输出结果如下:

    在这里插入图片描述

    如下代码所示,如果基类中不含有虚函数,则 typeid() 返回的是表达式的静态类型(即定义的类型),既然是定义的类型,那么编译器不需要对表达式求值也能知道表达式的静态类型。

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    输出结果如下:

    在这里插入图片描述

    4.RTTI与虚函数表

    #include 
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    在 C++ 中,如果类中含有虚函数,那么编译器就会对该类产生一个虚函数表。

    在上面代码中,pa 指向一个 Dog 类型的对象,该对象里有一个指针,称为虚函数表指针,虚函数表指针指向的是该对象所在的类 Dog 的虚函数表。

    虚函数表里有很多项,每一项都是一个指针,每个指针指向的是这个类中的各个虚函数的入口地址。

    虚函数表中的第一个表项很特殊,它指向的不是虚函数的入口地址,它指向的实际上是这个类所关联的 type_info 对象。

  • 相关阅读:
    拆解现货黄金隔夜利息计算公式
    使用elementUI的form表单和Steps步骤条如何让rules分步骤校验
    java有关的HttpsUtils工具类 https请求工具类
    云原生 黑马Kubernetes教程(K8S教程)笔记——第一章 kubernetes介绍——Master集群控制节点、Node工作负载节点、Pod控制单元
    【问题思考】如何通过参数式求出方向向量?(待深入本质)
    QT实现自定义带有提示信息的透明环形进度条
    SpringBoot 关于异步与事务一起使用的问题
    一个颜值功能双在线的Zookeeper可视化工具
    关于ElasticSearch版本7.8.0进行排坑
    Mac安装CocoaPods
  • 原文地址:https://blog.csdn.net/qq_42815188/article/details/85876833