(转)RTTI特性小究(dynamic_cast转换操作符和typeid操作符)

来源:互联网 发布:java最有名的论坛 编辑:程序博客网 时间:2024/04/30 00:35

文章转自:http://hi.baidu.com/tangliangl_cv/blog/item/21d3c019c5c28d4643a9adf4.html

要使用RTTI特性,必须包含<typeinfo>头文件,并且要使用/GR开启运行时信息,

在visual studio中

打开该项目的“属性页”对话框。有关详细信息,请参见如何:打开项目属性页。

单击“C/C++”文件夹。

单击“语言”属性页。

修改“启用运行时类型信息”属性。

在g++中应该是在编译时使用命令/GR即可,(这个本人不是很确定,没实验过)

先给一小段代码:

             class A
{
public:
virtual ~A()
{}
};
class B:public A
{
};

            一: dynamic_cast强制转换运算符将一个基类的指针或者引用转换为其子类的指针或者引用,其使用方法为:dynamic<type>(object).注意:基类必须含有虚函数(如果没有虚函数,可将析构函数声明为virtual,后面会解释为什么需要虚函数),对该基类的指针或引用使用后会出现以下几种情况:

1.将一个基类的指针转换为子类的指针时,如果基类指针指向的是其子类的一个对象,则转换成功,返回一个指向子类的指针;否则转换不成功,返回一个NULL指针。例如:

A *pa = new B();

B *pb = dynamic_cast<B*>(pa);

delete pa;

此处由于pa指向一个B类的对象,所以转换成功。下面的例子将转换不成功,返回NULL指针:

A p;
B *pb = dynamic_cast<B*>(&p);
if(pb == NULL)
{
           cout<<"p is not a B object"<<endl;
}
else
{
           cout<<"pa is a B object"<<endl;
}

输出:p is not a B object

2.将一个基类的引用转换为子类的引用时,如果基类引用的是一个子类对象,那么转换会成功,返回一个子类的引用;否则转换不成功,dynamic_cast将抛出一个bad_cast异常.例如:

A p;

A& a = p;
try
{
           B& b = dynamic_cast<B&>(a);
           cout<<"a is a reference of type B"<<endl;
}
catch(bad_cast)
{
           cout<<"a is not a reference of type B"<<endl;
}

由于a引用的是一个类型A的对象,故转换不成功,抛出一个bad_cast异常,因此该段代码的输出是:a is not a reference of type B

现在解释为什么dynamic_cast运算符必须要求基类中有虚函数,下边的分析是我的看法:

一个对象的类型信息是被虚表(virtual table)的第一个槽中(first slot)的指针指向的,而虚表又是由对象模型中的vptr(虚表指针)指向的,因此要获得对象的类型信息,则要求基类必须拥有一个vptr,也就是有至少一个虚函数,这样才能在运行时获得virtual table中指向的类型信息。确定基类指针或引用是否指向的是一个子类对象。关于虚表和虚表指针在对象模型中的分布,可以查看Inside the C++ object model,里边有详细的说明。

不知道上边有没有把话说清楚

二:typeid运算符是获得一个类型或者对象的运行时信息,使用方法如下:

typeid( type-id ),typeid( expression ),返回的是一个类型为const type_info&的对象,type_info类有几个成员函数:

bool operator==(const type_info& rhs) const;
bool operator!=(const type_info& rhs) const;
int before(const type_info& rhs) const;
const char* name() const;

每个成员函数的作用可查看msdn,该类有一个成员函数const char* type_info::name(),
使用它可以得到该类型的名字,例如:

A *a = new B();
cout<<typeid(*a).name()<<endl;

在vc2005上输出:class B,而不是class A,因为a指向的是一个B类型的对象。

在g++或者别的编译器上可能输出B,1B等经过name-mangling后的类型名,不过大致上还是趋于类型原名的

是一个空指针或者野指针,我们要通过它来查看它所指向的类型时,这时会抛出一个bad_typeid异常,例如:
A* pa = NULL;
try
{
        cout<<typeid(&pa).name()<<endl;
        cout<<typeid(*pa).name()<<endl;
}
catch(bad_typeid)
{
        cout<<"*pa is a bad typeid"<<endl;
}

输出:class A ** 换行后再输出*pa is a bad typeid.


dynamic_cast操作符只处理对象,即:dynamic_cast<type>(object),而typeid可以直接以类型和对象作为操作数,对象可以是基本类型,包括整型,浮点型,char型等等,即:typeid(type)或者typeid(expression),它们多是在运行时才能获得类型的信息,而非在静态编译时即可获得。前面说了那么多关于typeid的用法,那什么时候我们才可能用到typeid运算符呢?

下边一小段代码用于显示每个类型的信息:

void DisplayInfo(const string& rhs)
{
if(rhs == typeid(Employee).name())
{
        Employee empl;
        empl.Display();
}
else if(rhs == typeid(Department).name())
{
        Department dept;
        dept.Display();
}
else if(rhs == typeid(Project).name())
{
        Project proj;
        proj.Display();
}
}这会应该能大概看出它的作用了吧?

原创粉丝点击