运行时类型识别(RTTI)与动态类型转换原理(reinterpret_cast const_cast static_cast dynamic_cast)

RTTI的三个作用

(1)配合typeid操作符的实现

(2)实现异常处理中catch 的匹配过程

(3)实现动态类型转换dynamic_cast

1.typeid操作符的实现

(1)静态类型

使用typeid关键字来获取对象类型的信息,返回值是const std::type_info&

#include<iostream>
using namespace std;
#include<assert.h>
struct B{}b,c;
struct D:B{}d;
void main()
{
	const std::type_info& tb= typeid(b);
	const std::type_info& tc= typeid(c);
	const std::type_info& td= typeid(d);
	assert(tb == tc);//相同类型
	assert(&tb == &tc);//引用也是相同的对象
	assert(tb != td);//b d类型不相同
	assert(&tb != &td);// b , d的引用也不相同

}

(二)动态类型的情形

typeid的操作数的引用是一个动态类(含有虚函数的类)类型时,返回值是被引用对象对应类型的类型信息对象

#include<iostream>
#include<typeinfo>
#include<assert.h>
using namespace std;
struct B
{
	virtual void foo(){}
};
struct C
{
	virtual void bar(){}
};
struct D:B, C{};
int main()
{
	D d;
	B& rb = d;
	C& rc = d;
	assert(typeid(rb) == typeid(d));//rb的引用类型与d相同
	assert(typeid(rb) == typeid(rc));//rb的引用类型与rc的引用类型相同
}


如果表达式的类型是类类型而且至少包含一个虚函数,typeid返回表达式的动态类型,需要在运行时计算,否则typeid操作符将会返回表达式的静态类型,在编译时计算

#include<iostream>
#include<typeinfo>
using namespace std;

class BaseA{};
class DerivedA:public BaseA{};
class BaseB
{
	virtual void fun()
	{}
};
class DerivedB:public BaseB
{

};

void main()
{
	cout<<"-----直接处理类名--------"<<endl;
	cout<<typeid(BaseA).name()<<endl;
	cout<<typeid(DerivedA).name()<<endl;
	cout<<typeid(BaseB).name()<<endl;
	cout<<typeid(DerivedB).name()<<endl;

	cout<<"-----基类不含虚函数------"<<endl;
	BaseA baseA;
	DerivedA derivedA;
	cout<<typeid(baseA).name()<<endl;
	cout<<typeid(derivedA).name()<<endl;



	BaseA *pa;
	pa = &baseA;
	cout<<typeid(*pa).name()<<endl;
	cout<<typeid(pa).name()<<endl;

	pa = &derivedA;
	cout<<typeid(*pa).name()<<endl;
	cout<<typeid(pa).name()<<endl;


	cout<<"---基类含有虚函数---"<<endl;
	BaseB baseB;
	DerivedB deriveB;
	cout<<typeid(baseB).name()<<endl;
	cout<<typeid(deriveB).name()<<endl;

	BaseB *pb;
	pb = &baseB;
	cout<<typeid(*pb).name()<<endl;
	cout<<typeid(pb).name()<<endl;

	pb = &deriveB;
	cout<<typeid(*pb).name()<<endl;
	cout<<typeid(pb).name()<<endl;

}


运行结果:


(1)当typeid 操作符的操作数是不带有虚函数的类类型时,typeid操作符会指出操作数的类型,而不是底层对象的类型

(2)如果typeid 操作符的操作数是至少包含一个虚函数的类类型时,并且该表达时是一个基类的引用,则typeid操作符指出底层对象的派生类类型


显式转换:

显示转换也称为强制类型转换(cast).包含以下强制类型转换的操作符

(1)static_cast

(2)dynamic_cast

(3)const_cast

(4)reinterpret_cast

转换的形式为:

cast_name<type>(expression)

cast_name为上述操作符的任意一个

type 为目标类型

expression 为被强制转换的表达式类型

(一)static_cast

(1)编译器隐式执行的任何类型转换都可以由static_cast来完成

#include<iostream>
using namespace std;
int main()
{
	double d = 97.0;
	int i = static_cast<int>(d);
	cout<<i<<endl;
}

(2)类层次之间上行/下行转换可以由static_cast 显式完成

但是(下行转换)把基类指针或引用转换为子类指针或者引用,由于没有动态类型检查,所以是不安全的(下行转换是不安全的)

#include<iostream>
#include<typeinfo>
using namespace std;
class Base{};
class Derived:public Base{};
int main()
{
	 Base *b = new Base;
	 cout<<typeid(b).name()<<endl;
	 Derived *c;
	 c = static_cast<Derived*>(b);
	 cout<<typeid(c).name()<<endl;

}

运行结果:



C++基本类型的指针之间不含有隐式类型转换

(二)dynamic

cast_name<type>(expression)

作用:把expression 转换为type类型的对象

type 必须是类的指针,引用或者void*

如果type是指针类型,那么expression也是指针类型

如果type是引用类型,那么expression也是引用类型

dynamic_cast 涉及类型安全检查,dynamic_cast运行时类型检查,需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表,对于没有虚函数表的类使用会导致dynamic_cast 编译错误

 

如果绑定到引用或者指针的对象类型不是目标类型,则dynamic_cast失败

如果转换到指针类型的dynamic_cast失败,则dynamic_cast的结果为0

如果转换到引用类型的dynamic_cast失败,则抛出一个bad_cast的异常

 

可以在执行期决定真正的类型

如果下行转换是安全的(基类的指针或者引用确实指向一个派生类的对象),这个运算符就会传回转型过的指针

如果不安全,就传回空指针

 

进行上行转换与static_cast效果相同

进行下行转换时,dynamic_cast有类型的检查功能, 比static_const 更加安全

接下来下面的例子更好的理解dynamic_cast

#include<iostream>
#include<typeinfo>
using namespace std;
class Base
{
public:
	Base():b(1){}
	virtual void foo()
	{}
	int b;

};
class Derived:public Base
{
public:
	Derived():d(2){}
	int d;
};
int main()
{
	 Base *pb = new Derived;
	 Derived *pd1 = static_cast<Derived*>(pb);
	 cout<<pd1->b<<endl;
	 cout<<pd1->d<<endl;

	 Derived *pd2 = dynamic_cast<Derived*>(pb);
	 cout<<pd2->b<<endl;
	 cout<<pd2->d<<endl;


}

运行结果:



分析:

Pd1和pd2是一样的,如果对这两个指针执行Derived类型的任何操作都是安全的,所以编译正常,运行正常,输出结果 正确

如果改为以下:

#include<iostream>
#include<typeinfo>
using namespace std;
class Base
{
public:
	Base():b(1){}
	virtual void foo()
	{}
	int b;

};
class Derived:public Base
{
public:
	Derived():d(2){}
	int d;
};
int main()
{
	 Base *pb = new Base;//改为指向base类型
	 Derived *pd1 = static_cast<Derived*>(pb);
	 cout<<pd1->b<<endl;
	 cout<<pd1->d<<endl;

	/* Derived *pd2 = dynamic_cast<Derived*>(pb);
	 cout<<pd2->b<<endl;
	 cout<<pd2->d<<endl;
	 */


}
结果:



分析:

Pd1是一个指向B对象的指针,对它进行D类型操作时,是不安全的,输出的d

的值就是一个垃圾值,但是static_cast没有类型安全检查,所以程序正常运行



#include<iostream>
#include<typeinfo>
using namespace std;
class Base
{
public:
	Base():b(1){}
	virtual void foo()
	{}
	int b;
};
class Derived:public Base

{
public:
       Derived():d(2){}
        int d;
};
int main()
{ 
      Base *pb = new Base; 
      /*Derived *pd1 = static_cast<Derived*>(pb); 
       cout<<pd1->b<<endl; 
       cout<<pd1->d<<endl;*/ 
      Derived *pd2 = dynamic_cast<Derived*>(pb);
      cout<<pd2->b<<endl; 
       cout<<pd2->d<<endl;
}

分析因为是不安全的,所以dynamic_cast会返回一个空指针,对空指针进行操作,就会发生异常

问题:

如果B中没有虚函数,会发生什么情况?

#include<iostream>
#include<typeinfo>
using namespace std;
class Base
{
public:
	Base():b(1){}
	//virtual void foo()
	//{}
	int b;

};
class Derived:public Base
{
public:
	Derived():d(2){}
	int d;
};
int main()
{
	 Base *pb = new Derived;
	 Derived *pd1 = static_cast<Derived*>(pb);
	 cout<<pd1->b<<endl;
	 cout<<pd1->d<<endl;

	/* Derived *pd2 = dynamic_cast<Derived*>(pb);
	 cout<<pd2->b<<endl;
	 cout<<pd2->d<<endl;
	 */
	 


}

编译结果:正常编译

运行结果:正常运行



static_cast没有必须有虚函数表的限制,所以正常编译,正常运行

如果改为下面代码:

#include<iostream>
#include<typeinfo>
using namespace std;
class Base
{
public:
	Base():b(1){}
	//virtual void foo()
	//{}
	int b;

};
class Derived:public Base
{
public:
	Derived():d(2){}
	int d;
};
int main()
{
	 Base *pb = new Derived;
	 /*Derived *pd1 = static_cast<Derived*>(pb);
	 cout<<pd1->b<<endl;
	 cout<<pd1->d<<endl;*/

	 Derived *pd2 = dynamic_cast<Derived*>(pb);
	 cout<<pd2->b<<endl;
	 cout<<pd2->d<<endl;
	 
}	 

编译结果:



分析:dynamic_cast运行时类型安全检查需要运行时类型信息,而这个信息存储在类的虚函数表中,只有定义了虚函数的类才有虚函数表,所以就会编译错误


#include<iostream>
using namespace std;

class Base1
{
	virtual void f1()
	{
		cout<<"Base1::f1"<<endl;
	}
};

class Base2
{
	virtual void f2()
	{
		cout<<"Base2:f2"<<endl;
	}
};

class Derived:public Base1, public Base2
{
	void f1()
	{
		cout<<"Derived::f1"<<endl;
	}
	void f2()
	{
		cout<<"Derived::f2"<<endl;
	}
};

int main()
{
	Base1 *pb= new Derived;
	Derived *pd1 = dynamic_cast<Derived*>(pb);
	Derived *pd2 = static_cast<Derived*>(pb);

	Base2 *pb1 = dynamic_cast<Base2*>(pb);
	Base2 *pb2 = static_cast<Base2*>(pb);

}


运行结果:



分析:对于多重继承,如果pb 真的指向Derived, 使用dynamic_cast 或者 static_cast都可以,但是如果要转化Base1 为其兄弟类Base2, 必须使用dynamic_cast,

使用static_cast无法编译


(三)const_cast

将转换表达式的const 性质

只有用const_cast才能将const性质转换掉,除了添加和删除const 特性,用const_cast符来执行其它任何类型的转换都会引起编译错误

const double  val = 0.1;
double *ptr = const_cast<double*>(&val);

(四)reinterpret_cast

显式强制类型转换用圆括号实现

int *p;
char *ptr = (char*)p;
效果与使用reinterpret相同
char *pc = reinterpret_cast<char*>(p);













  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值