dynamic_cast的详细用法,typeid运算符简介

本文详细介绍了C++中的dynamic_cast运算符和typeid运算符的使用。dynamic_cast用于安全地在类继承层次中进行类型转换,尤其是在指针或引用转换中,确保转换的正确性。当转换失败时,引用转换会抛出std::bad_cast异常,而指针转换则会得到空指针。typeid运算符则可以获取对象的实际类型信息,返回type_info对象,帮助开发者了解对象的类型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

dynamic_cast详细用法

代码演示:
下面根据这个例子讲:
father.h

#pragma once
#include <iostream>
using namespace std;

class Father
{
public:
	Father();
	virtual ~Father();
	virtual void play();
};

father.cpp

#include "Father.h"

Father::Father()
{
	cout << "调用了Father()构造函数" << endl;
}

Father::~Father()
{
	cout << "调用了Father()析构函数" << endl;
}

void Father::play()
{
	cout << "调用了Father---play()"<< endl;
}

son.h

#pragma once
#include "Father.h"

class Son : public Father
{
public:
	Son();
	~Son();
	void play();
	void SonFunc() { cout << "执行了SonFunc()函数" << endl; };
};

son.cpp

#include "Son.h"


void Son::play()
{

	cout << "调用了Son---play()" << endl;
}

Son::Son()
{
	cout << "调用了Son构造函数" << endl;
}

Son::~Son()
{
	cout << "调用了Son析构函数" << endl;
}

son2.h

#pragma once
#include "Father.h"
class Son2 :   public Father
{
public:

    Son2();
    ~Son2();
    void play();
    void Son2Func() { cout << "执行了Son2Func()函数" << endl; };
};

son2.cpp

#include "Son2.h"


void Son2::play()
{

	cout << "调用了Son---play()" << endl;
}

Son2::Son2()
{
	cout << "调用了Son构造函数" << endl;
}

Son2::~Son2()
{
	cout << "调用了Son析构函数" << endl;
}

main.cpp

#include "Son.h"
#include "Son2.h"
#include "Father.h"
#include <iostream>

int main()
{
	return 0;
}

共7个文件:Father.h/cpp; son.h/cpp; son2.h/cpp; main.cpp
代码内容并不多,函数体也只是打印一句执行信息; 以下测试代码均在main.cpp中进行

int main()
{
	Father father;
	Son son;
	Son2 son2;

	Father* tmp = new Son;  //父类的指针指向子类对象,此时指针可以调用子类的虚函数
	//tmp->SonFunc(); //报错   SonFunc()是子类的成员函数
	//父类指针指向子类对象,却只能访问子类中的虚函数,无法调用子类的成员函数
	return 0;
}

通过虚函数的学习,父类指针指向子类对象,此时父类指针可以调用子类中的虚函数,但是无法访问子类的其他成员函数(非虚函数),这时候的解决办法可能是:
(1)把这个函数在子类和父类中都写成虚函数,但是,这样做有些多余,而且显然,子类每增加一个新成员函数就要在父类中增加等同的虚函数,这种解决方案虽然可以,但不尽人意.
(2)可以使用dynamic_cast运算符进行类型转换.

dynamic_cast运算符:他的功能是将父类指针或者引用安全的转换为子类指针或者引用;

为了比较安全性,这里先从c语言风格的强制类型转换作为比较示例;

	Father* tmp = new Son;

	Son* p1 = (Son*)(tmp);  //c语言风格的强制类型转换
	p1->SonFunc();   //能够正常调用
	
	delete tmp;

上述c语言风格的强制类性转换中,因为代码是自己写的,所以程序员应该知道tmp是可以正常转换成Son* 类型的,因为是强制类型转换,所以你即使转换成Son2* 也是不会报错,甚至调用Son2中的成员函数.如下

	Father* tmp = new Son;  //注意这里,Son类型
	
	//因为强制类型转换,这里就算是转成Son2类型,也没有报错
	Son2* p2 = (Son2*)(tmp);
	p2->Son2Func();  //甚至调用成员函数语法也没有报错
	//但很有可能会出现问题,因为这个tmp是Son类型
	delete tmp;

为什么不会报错?

原因就是之前提到的,强制类型转换会干扰系统的正常类型检查, 很多异常转换编译器本身是会报错的, 但是一旦用了强制类型转换,就会抑制编译器的报错行为,简单说就是,只要强转,系统就会信任程序员.

所以,如果是别人的库,传递的指针,通过C语言中的强转也没法区分这个指针是子类类型还是父类类型,哪个子类,哪个父类.

因此,选择用dynamic_cast运算符就可以判断出来,dynamic_cast转换是否成功,可以确定实际要转换的类型(比如上述例子中,就能确定是Son类型指针)

示例

	Father* tmp = new Son;
	Son* p2 = dynamic_cast<Son*>(tmp);  //使用dynamic_cast必须包含一个虚函数

	if (p2 != nullptr)
	{
		cout << "p2是Son类型" << endl;
		p2->SonFunc();
	}
	else
	{
		cout << "p2不是Son类型 " << endl;
	}
	delete tmp;

运行结果
在这里插入图片描述

通过比较指针是否为空即可判断是否是正确的类型转换
dynamic_cast在这里就发挥了他的作用,当然这里要注意一点,使用dynamic_cast父类中至少要有一个虚函数,否则会报错

其他示例测试(错误的情况)

	Father* tmp = new Son;
	Son2* p2 = dynamic_cast<Son2*>(tmp);  //使用dynamic_cast必须包含一个虚函数

	if (p2 != nullptr)
	{
		cout << "p2是Son类型" << endl;
		p2->Son2Func();
	}
	else
	{
		cout << "p2不是Son类型 " << endl;
	}
	delete tmp;

显然,这里p2不是Son类型,转换失败
在这里插入图片描述
其他情况,父类转父类
很多余的操作,只是演示一下父类转父类也是可行的

	Father* tmp = new Son;
	Father* p2 = dynamic_cast<Father*>(tmp);  
	if (p2 != nullptr)
	{
		cout << "dynamic_cast转换成功" << endl;
	}
	else
	{
		cout << "dynamic_cast转换失败 " << endl;
	}

	delete tmp;
	return 0;

在这里插入图片描述

dynamic_cast针对"引用"的情况

上述例子可以看出, 通过指针类型判断是否为空指针即可,而引用中是没用空引用的,所以对于引用的情况,如果转换失败,程序会抛出一个std::bad_cast异常,这个异常在标准库头文件里是有定义的,捕捉异常常用try{}和catch(){}
示例

	Father* tmp = new Son;
	Father& p2 = *tmp;  //引用

	try
	{
		Son2& Sontmp = dynamic_cast<Son2 &>(p2);
		cout << "tmp是Son2类型" << endl;
	}
	catch (std::bad_cast)
	{
		cout << "tmp实际不是Son2类型" << endl;
	}

显然不是,是Son类型.正确的方式只需把Son2改成Son即可.

typeid运算符

typeid:
作用:返回指针或者引用所指对象的实际类型
用法:
typeid(类型).
typeif(表达式).

通过这个运算符,可以获取到对象的信息,这个运算符会返回一个常量对象的引用.
这个常量对象的类型一般是标准库类型type_info,其实type_info就是一个类类型.

示例:


		Father* tmp = new Son;
		Father& q = *tmp;

		cout << typeid(*tmp).name() << endl;
		cout << typeid(q).name() << endl;

		char a[10] = {5, 1};
		int b = 100;

		cout << typeid(a).name() << endl;
		cout << typeid(b).name() << endl;
		cout << typeid(1.25).name() << endl;
		cout << typeid("abc").name() << endl;

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值