RTTI与类型转换运算符复习

什么是RTTI﹖ 
     在C++ 环境中﹐头文件(header file) 含有类之定义(class definition)亦即包含有关类的结构资料(representational information)。但是﹐这些资料只供编译器(compiler)使用﹐编译完毕后并未留下来﹐所以在执行时期(at run-time) ﹐无法得知对象的类资料﹐包括类名称、数据成员名称与类型、函数名称与类型等等。例如﹐两个类Figure和Circle﹐其之间为继承关系。 
若有如下指令﹕ 
Figure *p; 
p = new Circle(); 
Figure &q = *p; 
    在执行时﹐p指向一个对象﹐但欲得知此对象之类资料﹐就有困难了。同样欲得知q 所参考(reference) 对象的类资料﹐也无法得到。RTTI(Run-Time Type Identification)就是要解决这困难﹐也就是在执行时﹐您想知道指针所指到或参考到的对象类型时﹐该对象有能力来告诉您。随着应用场合之不同﹐所需支持的RTTI范围也不同。最单纯的RTTI包括﹕ 
●类识别(class identification)──包括类名称或ID。 
●继承关系(inheritance relationship)──支持执行时期的「往下变换类型」(downward casting)﹐亦即动态变换类型(dynamic casting) 。

    在对象数据库存取上﹐还需要下述RTTI﹕ 
●对象结构(object layout) ──包括属性的类型、名称及其位置(position或offset)。
●成员函数表(table of functions)──包括函数的类型、名称、及其参数类型等。
    其目的是协助对象的I/O 和持久化(persistence) ﹐也提供调试讯息等。 
     若依照Bjarne Stroustrup 之建议〔注1 〕﹐C++ 还应包括更完整的RTTI﹕ 
●能得知类所实例化的各对象 。 
●能参考到函数的源代码。 
●能取得类的有关在线说明(on-line documentation) 。 
    其实这些都是C++ 编译完成时所丢弃的资料﹐如今只是希望寻找个途径来将之保留到执行期间。然而﹐要提供完整的RTTI﹐将会大幅提高C++ 的复杂度﹗

 

通过RTTI,能够通过基类的指针或引用来检索其所指对象的实际类型。c++通过下面两个操作符提供RTTI。

(1)typeid:返回指针或引用所指对象的实际类型。

(2)dynamic_cast:将基类类型的指针或引用安全的转换为派生类型的指针或引用。

对于带虚函数的类,在运行时执行RTTI操作符,返回动态类型信息;对于其他类型,在编译时执行RTTI,返回静态类型信息。

 

dynamic_cast 操作符

如果dynamic_cast转换指针类型失败,则返回0;如果转换引用类型失败,则抛出一个bad_cast类型的异常。

可以对值为0的指针使用dynamic_cast,结果为0。

dynamic_cast会首先验证转换是否有效,只有转换有效,操作符才进行实际的转换。

if (Derived *derivedPtr = dynamic_cast<Derived *>(basePtr))
{
    // use the Derived object to which derivedPtr points
}
else
{ // basePtr points at a Base object
    // use the Base object to which basePtr points
}

 

也可以使用dynamic_cast将基类引用转换为派生类引用:dynamic_cast<Type&>(val)

因为不存在空引用,所以不能像指针一样对转换结果进行判断。不过转换引用类型失败时,会抛出std::bad_cast异常。

try
{ 
    const Derived &d = dynamic_cast<const Derived&>(b);
}
catch (bad_cast) {
    // handle the fact that the cast failed.
}

注意:dynamic_cast在将父类cast到子类时。父类必需要有虚函数。比如在以下的代码中将CBasic类中的test函数不定义成 
       virtual时,编译器会报错:error C2683: dynamic_cast : “CBasic”不是多态类型 

 

typeid操作符

typeid能够获取一个表达式的类型:typeid(e)。

如果操作数不是类类型或者是没有虚函数的类,则获取其静态类型;如果操作数是定义了虚函数的类类型,则计算运行时类型。

typeid最常见的用途是比较两个表达式的类型,或者将表达式的类型与特定类型相比较。typeid运算符返回一个对type_info对象的引用。

可以:

typeid(*pg).name();

 

Base *bp;
Derived *dp;
// compare type at run time of two objects
if (typeid(*bp) == typeid(*dp))
{
    // bp and dp point to objects of the same type
}
// test whether run time type is a specific type
if (typeid(*bp) == typeid(Derived))
{
    // bp actually points a Derived
}

注意:如果是typeid(bp),则是对指针进行测试,这会返回指针(bp)的静态编译时类型(Base *)。

如果指针p的值是0,,并且指针所指的类型是带虚函数的类型,则typeid(*p)抛出一个bad_typeid异常。

 

type_info

type_info类的实现因编译器的不同而不同。但如下几个常用的操作符和函数是c++标准要求必须实现的:“t1 == t2”、“t1 != t2”、“t.name()”。

typeid操作符的返回类型就是type_info,正因为type_info提供了“==”操作符,才可以进行上面提到的“if (typeid(*bp) == typeid(*dp))”判断。

type_info的默认构造函数、拷贝构造函数、赋值操作符都定义为private,创建type_info对象的唯一方法就是使用typeid操作符。

name()函数返回类型名字的c-style字符串,但字符串的格式可能不同的编译器略有不同。下面是在vc2008编译器下的测试。

请注意*pb和pb    *pd和pd

// expre_typeid_Operator.cpp
// compile with: /GR /EHsc
#include <iostream>
#include <typeinfo.h>

class Base 
{
public:
    virtual void vvfunc() {}
};

class Derived : public Base {};

using namespace std;

int main() 
{
    Derived* pd = new Derived;
    Base* pb = pd;
    int i = 0;

    cout << typeid( i ).name() << endl;    // prints "int"
    cout << typeid( 3.14 ).name() << endl; // prints "double"
    cout << typeid( pb ).name() << endl;   // prints "class Base *"
    cout << typeid( *pb ).name() << endl;  // prints "class Derived"
    cout << typeid( pd ).name() << endl;   // prints "class Derived *"
    cout << typeid( *pd ).name() << endl;  // prints "class Derived"

    delete pd;
}

 

类型转换运算符 

C++中提供4中类型转运算符,分别是:static_cast、dynamic_cast、reinterpret_cast和const_cast;

1、static_cast可以完全替代c的类型转换,而且在对对象指针之间的类型转换时,可以将父类指针转换成子类指针,也可以将子类指针转换成父类指针,但是如果两个类不相关则无法相互转换。 需注意的是,如果父类指针指向一个父类对象,此时将父类指针转换成子类指针虽然可以通过static_cast实现,但是这种转换很不安全;如果父类指针本身就指向子类指针则不存在安全问题。编译时类型检查。

class Base(){};
class Derived:public Base{};

Base *b1 = new Base;
Base *b2 = new Derived;

Derived *b2d1 = static_cast<Derived*>(b1);   //转换成功不安全
Derived *b2d2 = static_cast<Derived*>(b2);    //转换成功安全

int i = 0;
double d = 1.9;

int d2i = static_cast<int>d;
double i2d = static_cast<double>i;

2.dynamic_cast 只能用于对象指针之间的类型转换,可以将父类指针转换成子类指针,也可以将子类指针转换成父类指针,转换结果也可以是引用,但是dynamic_cast不等同于static_cast。dynamic_cast在将父类指针转换为子类指针的过程中,需要对其背后的对象类型进行检查,以保证类型完全匹配,而static_cast不会。只有当一个父类指针指向一个子类对象,且父类中包含虚函数时,使用dynamic_cast将父类指针转换成子类指针才会成功,否则返回空指针,如果是引用则抛出异常。

3.const_cast转换过程中增加或删除const属性。

class Test{};
const Test *t1 = new Test;
Test *t2 = const_cast<Test*>(t1);      //转换成功

const_cast不是万能的。他可以修改指向一个值的指针,但是修改const值的结果是不确定的。如下:

#include<iostream>
using std::cout;
using std::endl;

void change(const int *pt, int n);

int main()
{
	int pop1 = 38383;
	const int pop2 = 2000;

	cout << "pop1,pop2: " << pop1 << ", " << pop2 << endl;
	change(&pop1, -103);
	change(&pop2, -103);
	cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl;
	
	return 0;
}

void change(const int * pt, int n)
{
	int *pc;

	pc = const_cast<int *>(pt);
	*pc += n;
}

由于pop2本身被生命为const,所以不能修改pop2。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值