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;