【C++grammar】动态类型转换、typeid与RTTI
动态类型转换
1、为何需要动态类型转换
定义一个函数:
void printObject(Shape& shape)
//shape是派生类对象的引用
{
cout<<"The area is"<<shape.getArea()<<endl;
//如果是circle,就输出半径
//如果是rectangle,就输出宽和高
}
如果这个shape指向的是一个circle类型的对象,我们想显示它的半径,该怎么办?
2、dynamic_cast<>();运算符
dynamic_cast<>();
沿着继承层次向上、向下以及侧向转换到类的指针和引用。
转指针:失败返回nullptr
转引用:失败抛异常
步骤:
1、先将shape类对象用dynamic_cast转换为派生类对象
2、然后调用派生类中独有的函数
如下所示:
void printObject(Shape& shape)
//shape是派生类对象的引用
{
cout<<"The area is"<<shape.getArea()<<endl;
//先获取基类指针
Shape *p = &shape;
//将基类指针转换为派生类指针
Circle *c = dynamic_cast<Circle*>(p);
//Circle& c = dynamic_cast<Circle&>(shape);
//引用转换失败则抛出一个异常 std::bad_cast
//如果shape是Circle对象,那么会成功,如果不是则不会成功
if(c != nullptr) //转换失败则指针为空
{
cout<<"The radius is"<<c->getRadius()<<endl;
}
}
3、向上转换和向下转换( Upcasting and Downcasting)
upcasting:将派生类类型指针赋值给基类类型指针。
downcasting : 将基类类型指针赋值给派生类类型指针。
|
|
Shape* s = nullptr;
Circle *c = new Circle(2);
s = c; //OK,隐式上转
2、下转必须显式执行
Shape* s = new Circle(1);
Circle *c = nullptr;
c = dynamic_cast <Circle*> (s); //显式下转
什么是侧向转换?
动态类型转换可以上转也可以下转。那么什么是侧向转换?
比如说circle和rectangle都是继承的shape,他们同级之间相互转换就是侧向转换。
4、 基类对象和派生类对象的互操作
之前的向上和向下转换都是以指针为例,现在不是指针了,而是对象。
首先看对象的内存布局:
首先定义一个父类和一个子类对象:
Shape S;
Circle C;
对象成员一览:
1、可以看出来派生类对象比基类对象多出来一点东西。
2、派生类里面包含了一个基类对象的数据
同时给出两个规则:
1、可将派生类对象截断,只使用继承来的信息
2、但不能将基类对象加长,无中生有变出派生类对象
了解到上面的知识之后,判断下面四个对错:
1、S = C;
2、C = S;
3、Shape &rS = C;
4、Circle &rC = S;
显然是13对(将派生类对象截断,只使用继承来的信息),24错(不能使基类对象加长,无中生有变出派生类对象)
5、Upcasting/Downcasting与继承链上不同类的对象之间的赋值有什么关系和区别?
向上向下转换只要是在同一个继承链上都是可以成立的。
而不同对象之间的赋值,只允许从下往上赋值,传递被继承的信息。
typeid 运行时查询类型的信息
typeid用于获取对象所属的类的信息:
(1) typeid returns a reference to an object of class type_info. (typeid运算符返回一个type_info对象的引用)
(2) typeid(AType).name() 返回实现定义的,含有类型名称的C风格字符串(char *)
#include <typeinfo> //使用typeid,需要包含此头文件
#include <iostream>
class A {};
A a{};
// ……
int main()
{
auto& t1 = typeid(a);
if (typeid(A) == t1) {
std::cout << "a has type "
<< t1.name() << std::endl;
}
}
结果:
RTTI与typeid 的关系
RTTI(Run-Time Type Identification),通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。
RTTI提供了以下两个非常有用的操作符:
(1)typeid操作符,返回指针和引用所指的实际类型。
(2)dynamic_cast操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用。
面向对象的编程语言,像C++,Java,delphi都提供了对RTTI的支持。 本文将简略介绍 RTTI 的一些背景知识、描述 RTTI 的概念,并通过具体例子和代码介绍什么时候使用以及如何使用 RTTI;本文还将详细描述两个重要的 RTTI 运算符的使用方法,它们是 typeid 和dynamic_cast。
其实,RTTI 在C++中并不是什么新的东西,它早在十多年以前就已经出现了。但是大多数开发人员,包括许多高层次的C++程序员对它并不怎么熟悉,更不用说使用 RTTI 来设计和编写应用程序了。
一些面向对象专家在传播自己的设计理念时,大多都主张在设计和开发中明智地使用虚拟成员函数,而不用 RTTI 机制。但是,在很多情况下,虚拟函数无法克服本身的局限。每每涉及到处理异类容器和根基类层次(如 MFC)时,不可避免要对对象类型进行动态判断,也就是动态类型的侦测。如何确定对象的动态类型呢?答案是使用内建的 RTTI 中的运算符:typeid 和 dynamic_cast。
在C++中存在虚函数,也就存在了多态性,对于多态性的对象,在程序编译时可能会出现无法确定对象的类型的情况。当类中含有虚函数时,其基类的指针就可以指向任何派生类的对象,这时就有可能不知道基类指针到底指向的是哪个对象的情况,类型的确定要在运行时利用运行时类型标识做出。