C++:关于C++的RTTI (运行阶段类型识别)
来源:互联网 发布:旅游策划师 知乎 编辑:程序博客网 时间:2024/05/17 18:46
RTTI 是运行阶段类型识别(Runtime Type Identification)的简称;
RTTI只适用于包含虚函数的类:
因为只有对于这种类层次结构,才能将派生类对象的地址赋给基类指针。
一,RTTI的用途
假设有一个类层次结构,其中的类都是从同一个基类派生而来的,则可以让基类指针指向其中任何一个类的对象。
这样便可以调用这样的函数:
在处理一些信息后,选择一个类,并创建这种类型的对象,然后返回它的地址,而该地址可以被赋给一个基类指针。
问题:如何知道指针指向的是哪种对象呢?
首先,要确定其类型。
如是想调用类方法的正确挑版本。
1)该函数是类层次结构中所有成员都拥有的虚函数,则并不真正需要知道其对象类型
2)该函数是派生类对象的方法,而不是继承而是来的。
在这种情况下,只有某些类型的对象可以使用该方法。也可能是出于调试的目的,想跟踪生成对象的类型。
这两种情况下,RTTI提供了解决方案。
二,RTTI的工作原理
C++有3个支持RTTI的元素。
1)dynamic_cast该运算符将使一个指向基类的指针来生成一个指向派生类的指针,否则,该运算符返回0--空指针。
2)typeid 该运算符返回 一个指出对象的类型的值。
3)type_info该结构存储了有关特定类型的信息。
详细介绍:
1,dynamic_cast运算符
dynamic_cast是最常用的RTTI组件,它不能回答“指针指向是哪类对象”,但能够回答:“是否可以安全将对象的地址赋给特定类型的指针”;
class Grand{ // has virtual methods };class Superb : public Grand { ... };class Magnificent : public Superb { ... };...Grand * pg = new Grand;Grand * ps = new Superb;Grand * pm = Magnificent;...Magnificent * p1 = (Magnificent *) pm; // #1Magnificent * p2 = (Magnificent *) pg; // #2Superb * p3 = (Magnificent *) pm; // #3#1:安全,因为它将Magnificent类型的指针指向Manificent类型的对象。
#2:不安全,因为它将基类对象Grand的地址赋给派生类(Magnificent)指针。这里程序将希望基类对象有派生类的特性,而这通常是不可能的。
#3:安全,因为它将派生类对象的地址赋给基类指针。即公有派生确保Magnificent对象同时也是一个Superb对象(直接基类)与一个Grand对象(间接基类)。
因此将它的地址赋给这三种类型的指针都是安全的。
通常想知道类型的原因在于:
知道类型后,就可以知道调用特定的方法是否安全。
要调用方法,并不一定要完全匹配,而可以是定义了该方法的虚拟版本的基本类型。
使用的基本方法:
Superb * pm = dynamic_cast<Superb *>(pb);这里,指针pg能否被安全的转换为Superb*?如果可以,运算符将返回对象的地址,否则返回一个0,即空指针。
例:
#include <iostream>#include <cstdlib>#include <ctime> // time(),srand(),rand()using std::cout;class Grand{private:int hold;public:Grand(int h = 0) : hold(h) {}virtual void Speak() const{cout << "I am a grand class!\n";}virtual int Value() const{return hold;}};class Superb : public Grand{public:Superb(int h = 0) : Grand(h) {}void Speak() const{cout << "I am a superb class!\n";}virtual void Say() const{cout << "I hold the superb value of " << Value() << "!\n";}};class Magnificent : public Superb{private:char ch;public:Magnificent(int h = 0, char c = 'A') : Superb(h), ch(c) {}void Speak() const {cout << "I am a magnificent class!\n";}void Say() const {cout << "I hold the character " << ch << " and the integer " << Value() << "!\n";}};Grand * GetOne();int main(){std::srand((unsigned)std::time(0));Grand * pg;Superb * ps;for (int i = 0; i < 5; ++i){pg = GetOne();pg->Speak();if (ps = dynamic_cast<Superb *>(pg)) //无目的赋值,有的通常用 ==ps->Say();}return 0;}Grand * GetOne(){Grand * p = NULL;switch (std::rand() % 3){case 0:{p = new Grand(std::rand() % 100);}break;case 1:{p = new Superb(std::rand() % 100);}break;case 2:{p = new Magnificent(std::rand() % 100,'A' + std::rand() % 26);}break;}return p;}
输出结果:
I am a grand class!I am a superb class!I hold the superb value of 29!I am a magnificent class!I hold the character Y and the integer 94!I am a superb class!I hold the superb value of 36!I am a magnificent class!I hold the character U and the integer 2!请按任意键继续. . .上面为指针的用法,也可以用引用,但用法稍微不同:
因为没有与空指针对应的引用值,因此无法使用特殊的引用值来指示失败,
当请求失败时,dynamic_cast 将引发类型为 bad_cast 的异常,这种异常从 exception 类派生而来,在头文件typeinfo中定义。
引用用法:
#include <typeinfo> // for bad_cast...try{Superb & rs = dynamic_cast<Superb &>(rg); //假设rg为Grand对象的引用...}catch(bad_cast &){...}2,typeid 运算符和 type_info 类
typeid 运算符能够确定两个对象是否为同一类型,接受两种参数:
1)类名;
2)结果为对象的表达式
typeid 运算符返回一个对 type_info对象的引用;
type_info在头文件typeinfo中定义的,并定义了 == 与 != 运算符,以便对类型进行比较。
例:
typeid(Magnificent) == typeid(*pg)
// 如果pg指向的是一个Magnificent 对象,则表达式为 true,否则为 false
// 但如果pg为一个空指针,则程序将引发 bad_typeid 异常。该异常也是从exception类中派生的,在头文件typeinfo中声明的。type_info 类的实现随厂商而异,但包含一个name()成员,该函数返回一个随实现而异的字符串,通常为(但并非一定是)类的名称;
例:
cout << "Now processing type " << typeid(*pg).name() << ".\n":
例:与上例一样,只是改了main()函数
int main(){std::srand((unsigned)std::time(0));Grand * pg;Superb * ps;for (int i = 0; i < 5; ++i){pg = GetOne();cout << "Now processing type " << typeid(*pg).name() << ".\n"; // #1pg->Speak();if (ps = dynamic_cast<Superb *>(pg))ps->Say();if (typeid(Magnificent) == typeid(*pg)) // #2cout << "Yes, you are really magnificent.\n"}return 0;}3,放弃使用dynamic_cast ,只使用typeid的方法
如上例的核心代码:
Grand * pg;Superb * ps;for (int i = 0; i < 5; ++i){pg = GetOne();pg->Speak();if (ps = dynamic_cast<Superb *>(pg))ps->Say();}放弃使用dynamic_cast ,则
Grand * pg;Superb * ps;Magnificent * pm;for (int i = 0; i < 5; ++i){pg = GetOne();if (typeid(Magnificent) == typeid(*pg)){pm = (Magnificent *)pg;pm->Speak();pm->Say();}else if (typeid(Superb) == typeid(*pg)){ps = (Superb *)ps;ps->Speak();ps->Say();}else{pg->Speak();}}缺点:更长,更难看,不易修改。
- C++:关于C++的RTTI (运行阶段类型识别)
- RTTI 运行阶段类型识别
- RTTI 运行阶段类型识别
- RTTI-运行阶段类型识别及类型转换
- [c++ primer plus]RTTI运行阶段类型识别
- RTTI运行阶段类型识别(Runtime Type Identification)
- 运行时类型识别(RTTI)
- RTTI 运行时类型识别
- 运行时类型识别(RTTI)
- 运行时类型识别RTTI
- RTTI 运行时类型识别
- RTTI运行期类型识别
- RTTI 运行时类型识别
- RTTI 运行时类型识别
- RTTI 运行时类型识别
- RTTI运行时类型识别
- 运行时类型识别(RTTI)
- RTTI 运行时类型识别
- 快餐生活时代
- Android通过POST方式传递数据到服务器简单demo
- bootstrap兼容IE6、7、8版本(IE9以下版本)
- 自测4. Have Fun with Numbers
- SAP NOTE 691154 DATA_TRANSFORM和 HIER_TRANSFORM 的说明
- C++:关于C++的RTTI (运行阶段类型识别)
- openCV学习笔记(7):cvRectangle与cv::rectangle的用法
- 最短路径问题
- 用数组来处理求Fibonacci数列问题
- android动态加载已安装apk中的方法
- hdu 1233 还是畅通工程
- iframe父子页面互相调用、控制
- 深度学习介绍(四)卷积操作
- xxx定律 HDU3782