概述
RTTI
通过运行时类型识别(RTTI),程序能够使用基类的指针或引用来检索这些
指针或引用所指对象的实际派生类型。
通过下面两个操作符提供 RTTI:
1. typeid 操作符,返回指针或引用所指对象的实际类型。
2. dynamic_cast 操作符,将基类类型的指针或引用安全地转换为派生类型
的指针或引用。
这些操作符只为带有一个或多个虚函数的类返回动态类型信
息
,对于其他类型,返回静态(即编译时)类型的信息。
对于带虚函数的类,在运行时执行 RTTI 操作符,但对于其他类型,在编译
时计算 RTTI 操作符。
当具有基类的引用或指针,但需要执行不是基类组成部分的派生类操作的时
候,需要动态的强制类型转换。通常,从基类指针获得派生类行为最好的方法是
通过虚函数。当使用虚函数的时候,编译器自动根据对象的实际类型选择正确的
函数。
但是,在某些情况下,不可能使用虚函数。在这些情况下,RTTI 提供了可
选的机制。然而,这种机制比使用虚函数更容易出错:程序员必须知道应该将对
象强制转换为哪种类型,并且必须检查转换是否成功执行了。
使用动态强制类型转换要小心。只要有可能,定义和使用虚函
数比直接接管类型管理好得多。
dynamic_cast
将基类类型对象的引用或指针转换为同一
继承层次中其他类型的引用或指针。
注:传入dynamic_cast的参数对应的类,必须带有virtual函数,否则,编译无法通过。
dynamic_cast 涉及运行时类型检查。如果绑定
到引用或指针的对象不是目标类型的对象,则 dynamic_cast 失败。如果转换到
指针类型的 dynamic_cast 失败,则 dynamic_cast 的结果是 0 值;如果转换
到引用类型的 dynamic_cast 失败,则抛出一个 bad_cast 类型的异常。
typeid 操作符
typeid 表达式形如:
typeid(e);
这里 e 是任意表达式或者是类型名。
如果表达式的类型是类类型且该类包含一个或多个虚函数,则表达式的动态类型可能不同于它的静态编译时类型
;
typeid 操作符可以与任何类型的表达式一起使用。内置类型的表达式以及
常量都可以用作 typeid 操作符的操作数。如果操作数不是类类型或者是没有虚
函数的类,则 typeid 操作符指出操作数的静态类型;如果操作数是定义了至少
一个虚函数的类类型,则在运行时计算类型。
typeid 操作符的结果是名为 type_info 的标准库类型的对象引用,
要使用 type_info 类,必须包含库头文件
typeinfo。
typeid 最常见的用途是比较两个表达式的类型,或者将表达式的类型与特
定类型相比较:
只有当 typeid 的操作数是带虚函数的类类型的对象的时候,
才返回动态类型信息。测试指针(相对于指针指向的对象)返
回指针的静态的、编译时类型。
如果指针 p 的值是 0,那么,如果 p 的类型是带虚函数的类型,则
typeid(*p) 抛出一个 bad_typeid 异常;如果 p 的类型没有定义任何虚函数,
则结果与 p 的值是不相关的。正像计算表达式 sizeof一样,编
译器不计算 *p,它使用 p 的静态类型,这并不要求 p 本身是有效指针。
实例
#include <iostream>
#include <typeinfo>
using namespace std;
class Base
{
public:
virtual void func(){cout << "I, Base." << endl;}
};
class Derived:public Base
{
public:
void dfunc(){cout << "I, Derived." << endl;}
};
class BadDerived
{
public:
virtual void func(){cout << "I, BadDerived." << endl;}
};
class T
{
public:
void dfunc(){cout << "I, T." << endl;}
};
int main()
{
Base *baseptr = new Derived;
BadDerived *badptr = new BadDerived;
cout << endl << "*********Test 0************" << endl;
if(T *deriveptr = dynamic_cast<T*>(baseptr))
{
cout << "success---->";
deriveptr->dfunc();
}
else
{
cout << "failed----->";
baseptr->func();
cout << "DEBUG" << endl;
cout << "typeid(T).name = " << typeid(T).name() << endl;
cout << "typeid(Baseref).name = " << typeid(*baseptr).name() << endl;
}
cout << endl << "*********Test 1************" << endl;
if(Derived *deriveptr = dynamic_cast<Derived*>(baseptr))
{
cout << "success---->";
deriveptr->dfunc();
}
else
{
cout << "failed----->";
baseptr->func();
cout << "DEBUG" << endl;
cout << "typeid(Derived).name = " << typeid(Derived).name() << endl;
cout << "typeid(Baseref).name = " << typeid(*baseptr).name() << endl;
}
cout << endl << "*********Test 2************" << endl;
if(Derived *deriveptr = dynamic_cast<Derived*>(badptr))
{
cout << "success---->";
deriveptr->dfunc();
}
else
{
cout << "failed----->";
badptr->func();
cout << "DEBUG" << endl;
cout << "typeid(Derived).name = " << typeid(Derived).name() << endl;
cout << "typeid(Baseref).name = " << typeid(*badptr).name() << endl;
}
Derived deriveobj;
Base &baseref = deriveobj;
BadDerived badobj;
BadDerived &badref = badobj;
cout << endl << "*********Test 3************" << endl;
try
{
Derived &deriveref = dynamic_cast<Derived&>(baseref);
cout << "success---->";
deriveref.dfunc();
}
catch(bad_cast)
{
cout << "failed----->";
baseref.func();
cout << "DEBUG" << endl;
cout << "typeid(Derived).name = " << typeid(Derived).name() << endl;
cout << "typeid(Baseref).name = " << typeid(baseref).name() << endl;
}
cout << endl << "*********Test 4************" << endl;
try
{
Derived &deriveref = dynamic_cast<Derived&>(badref);
cout << "success---->";
deriveref.dfunc();
}
catch(bad_cast)
{
cout << "failed----->";
baseref.func();
cout << "DEBUG" << endl;
cout << "typeid(Derived).name = " << typeid(Derived).name() << endl;
cout << "typeid(Baseref).name = " << typeid(badref).name() << endl;
}
cout << endl << "*********Test 5************" << endl;
if(typeid(Derived) == typeid(baseref))
{
Derived &deriveref = dynamic_cast<Derived&>(baseref);
cout << "success---->";
deriveref.dfunc();
}
else
{
cout << "failed----->";
baseref.func();
cout << "DEBUG" << endl;
cout << "typeid(Derived).name = " << typeid(Derived).name() << endl;
cout << "typeid(Baseref).name = " << typeid(baseref).name() << endl;
}
cout << endl << "*********Test 6************" << endl;
if(typeid(Derived) == typeid(badref))
{
Derived &deriveref = dynamic_cast<Derived&>(badref);
cout << "success---->";
deriveref.dfunc();
}
else
{
cout << "failed----->";
badref.func();
cout << "DEBUG" << endl;
cout << "typeid(Derived).name = " << typeid(Derived).name() << endl;
cout << "typeid(Baseref).name = " << typeid(badref).name() << endl;
}
}
执行结果
*********Test 0************
failed----->I, Base.
DEBUG
typeid(T).name = 1T
typeid(Baseref).name = 7Derived
*********Test 1************
success---->I, Derived.
*********Test 2************
failed----->I, BadDerived.
DEBUG
typeid(Derived).name = 7Derived
typeid(Baseref).name = 10BadDerived
*********Test 3************
success---->I, Derived.
*********Test 4************
failed----->I, Base.
DEBUG
typeid(Derived).name = 7Derived
typeid(Baseref).name = 10BadDerived
*********Test 5************
success---->I, Derived.
*********Test 6************
failed----->I, BadDerived.
DEBUG
typeid(Derived).name = 7Derived
typeid(Baseref).name = 10BadDerived
failed----->I, Base.
DEBUG
typeid(T).name = 1T
typeid(Baseref).name = 7Derived
*********Test 1************
success---->I, Derived.
*********Test 2************
failed----->I, BadDerived.
DEBUG
typeid(Derived).name = 7Derived
typeid(Baseref).name = 10BadDerived
*********Test 3************
success---->I, Derived.
*********Test 4************
failed----->I, Base.
DEBUG
typeid(Derived).name = 7Derived
typeid(Baseref).name = 10BadDerived
*********Test 5************
success---->I, Derived.
*********Test 6************
failed----->I, BadDerived.
DEBUG
typeid(Derived).name = 7Derived
typeid(Baseref).name = 10BadDerived
类型转换
C
风格转换是“万能的转换”,但需要程序员把握转换的安全性,编译器无能为力。
类型转换的不安全来源于两个方面:其一是类型的窄化转化,会导致数据位数的丢失;其二是在类继承链中,将父类对象的地址(指针)强制转化成子类的地址(指针),这就是所谓的下行转换,“下”表示沿着继承链向下走(向子类的方向走)。类似地,上行转换的“上”表示沿继承链向上走(向父类的方向走);上行转换一般是安全的,下行转换很可能是不安全的;因为子类中包含父类,所以上行转换(只能调用父类的方法,引用父类的成员变量)一般是安全的。但父类中却没有子类的任何信息,而下行转换会调用到子类的方法、引用子类的成员变量,这些父类都没有,所以很容易“指鹿为马”或者干脆指向不存在的内存空间。
static_cast <new_type> (expression) 静态转换
static_cast
最接近于
C
风格转换,但在无关类指针转换时,编译器会报错,提升了安全性。
dynamic_cast <new_type> (expression) 动态转换
动态转换确保类指针的转换是合适完整的,它有两个重要的约束条件,其一是
要求new_type为指针或引用,其二是下行转换时
要求基类是多态的(基类中包含至少一个虚函数),
如果发现下行转换不安全,
dynamic_cast
返回一个
null
指针。当待转换指针是void*或者转换目标指针是void*时,dynamic_cast总是认为是安全的。
reinterpret_cast <new_type> (expression) 重解释转换
这个转换是最“不安全”的,两个没有任何关系的类指针之间转换都可以用这个转换实现。reinterpret_cast可以把整型数转换成地址(指针),这种转换在系统底层的操作,有极强的平台依赖性,移植性不好;它同样
要求new_type是指针或引用。
const_cast <new_type> (expression) 常量向非常量转换
const_cast
可以将常量转成非常量,但不会破坏原常量的
const
属性,只是返回一个去掉
const
的变量。从char *cc = const_cast<char *>(c)可以看出了这个转换的作用了,但切记,
这个转换并不转换原常量本身,即c还是常量,
只是它返回的结果cc是非常量了。
最后
以上就是传统万宝路为你收集整理的RTTI和类型转换的全部内容,希望文章能够帮你解决RTTI和类型转换所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复