现在的位置: 首页 > 综合 > 正文

关于typeid —— typeid和RTTI C++

2013年01月13日 ⁄ 综合 ⁄ 共 4393字 ⁄ 字号 评论关闭

 

观点有一些值得商榷的地方

关于typeid和RTTI的问答
问:在c++里怎么能知道一个变量的具体类型,如:c#里的typeof.还有我怎么知道一个变量的类型是某个类型的子类,也就是实现关键字IS

答:
1。运行时获知变量类型名称,可以使用 typeid(变量).name,需要注意不是所有编译器都输出"int"、"float"等之类的名称,对于这类的编译器可以这样使用:float f = 1.1f; if( typeid(f) == typeid(0.0f) ) ……
2。对于多态类实例,想得到实际的类名称,需要使用到RTTI,这需要在编译的时候加上参数"/GR"。
3。对于普通变量,既然是自己写的,那当然也就应该知道它的类型,其实用不着运行时获知;对于多态类实例,既然需要运行时获知实际类型,那么就说明这里不具有多态性,既然没有多态性就不应该抽象它,这属于设计错误,总之,我认为RTTI是多余的。
4。对于多态类实例,使用 typeid(value) == typeid(value)来判断,不如使用 dynamic_cast 来判断,它们的原理是一样的。

事例代码:
#include
using namespace std;
int main( void )
{
// sample 1
       cout << typeid(1.1f).name() << endl;
// sample 2
       class Base1
       {
       };
       class Derive1 : public Base1
       {
       };
       Derive1 d1;
       Base1& b1 = d1;
       cout << typeid(b1).name() << endl; // 输出"class Base1",因为Derive1和Base1之间没有多态性
// sample 3, 编译时需要加参数 /GR
       class Base2
       {
           virtual void fun( void ) {}
       };
       class Derive2 : public Base2
       {
       };
       Derive2 d2;
       Base2& b2 = d2;
       cout << typeid(b2).name() << endl; // 输出"class Derive2",因为Derive1和Base1之间有了多态性
// sample 4
       class Derive22 : public Base2
       {
       };
       // 指针强制转化失败后可以比较指针是否为零,而引用却没办法,所以引用制转化失败后抛出异常
       Derive2* pb1 = dynamic_cast(&b2);
       cout << boolalpha << (0!=pb1) << endl; // 输出"true",因为b2本身就确实是Derive2
       Derive22* pb2 = dynamic_cast(&b2);
       cout << boolalpha << (0!=pb2) << endl; // 输出"true",因为b2本身不是Derive2

       try {
           Derive2& rb1 = dynamic_cast(b2);
           cout << "true" << endl;
       } catch( bad_cast )
       {
           cout << "false" << endl;
       }
       try {
           Derive22& rb2 = dynamic_cast(b2);
           cout << "true" << endl;
       } catch( ... ) // 应该是 bad_cast,但不知道为什么在VC++6.0中却不行
       {
           cout << "false" << endl;
       }

       return 0;
}

 

 

 

 

首先,很不好意思的说明,我还正在看C++ language programming,但还没有看到关于RTTI的章节。另外,我也很少使用C++ RTTI的特性。所以对RTTI的理解仅限于自己的摸索和思考。如果不正确,请大家指正。

         RTTI特性是C++语言加入较晚的特性之一。和其他语言(比如JAVA)相比,C++的RTTI能力算是非常差的。这与C++的设计要求应该有重要的关系:性能。没错,性能的因素使得C++的很多地方不能称的上完美,但是也正因为如此,在高级通用语言里面,只有C能和C++的性能可以相提并论。

 

1:typeid的研究(这部分是错的,关于错误的点我会标记并指出来,原文忘了是哪儿了,只能在原作者的基础上改下,修改见下面红字和留言,黑色字体是原文作者所写,没有改变,红色字体是我添加的,谢了留言板上那哥的提醒让我重新查了下)

        在C++中,似乎与RTTI相关的只有一个东西,就是dynamic_cast,本来我认为typeid是RTTI的一部分,但是我的实验表明,并非如此。typeid的操作是在编译时期就已经决定的了。下面的代码可以证明:

#include
#include

class A
{

    //缺少构造函数和析构函数

    A(){};                                //如果为A();没有定义会出现连接错误,下同

    virtual ~A(){};                    //ISO C++98 规定,涉及继承的 RTTI都要求对象是“多态类型”(见留言)
};

class B:public A
{

    //缺少构造函数和析构函数

    B(){};                                //同上

    virtual ~B(){};                    //同上
};

int main()
{
      A *pa;
      B b,*pb;
      pb = &b;
      pa = pb;

 

      //测试1
      std::cout<<"Name1:"<< (typeid(pa).name())<<endl                           //输出Name1:Class A *
           <<"Name2:"<<(typeid(pb).name())<<endl;                                  //输出Name2:Class B *

      std::cout<<"pa == pb:"<< (typeid(pa) == typeid(pb))<<:endl;            //输出pa == pb:0

 

      //测试2

      //但是若要判断能否判断得出指向的实质内容,应该用下面的输出(注:改变的是typeid()的参数内容,一个为指针p,一个为实例*p)

      //所以原作者以测试1为依据得到的结论是错误的,下面带有注释

      std::cout<<"Name1:"<< (typeid(*pa).name())<<endl                           //输出Name1:Class B *
           <<"Name2:"<<(typeid(*pb).name())<<endl;                                  //输出Name2:Class B *

      std::cout<<"*pa == *pb:"<< (typeid(*pa) == typeid(*pb))<<:endl;       //输出*pa == *pb:1

      return 0;
}

typeid根本不能判别pa实际上是一个B*(这句话时错误的,因为原文作者typeid的只是A指针pa和B指针pb,而不是typeid的A指针pa指向的内容*pa和B指针pb指向的内容*pb,所以这个结论我认为是错的)换句话说,typeid是以字面意思去解释类型,不要指望它能认出一个void*实际上是int*(这个连人也做不到:P)。实际上实用价值不大。

当然,在某些特殊地方,也是能够有些效用的,比如模板。

template
void test(T t)
{
if(typeid(t) == typeid(char *))
{
      // 对char *特殊处理
}
//...
}

如果编译器优化的好的话,并不会产生废代码,因为typeid编译时期就可以决定了。

 

2:dynamic_cast

       抱歉现在才讲到正题,我对dynamic_cast第一印象就是,它究竟是怎么实现的呢?经过一些思考,我认为最简单的方案就是将信息保存在vtable里,它会占用一个vtalbe表的项目。实验和书籍也证明了这一点。但是就会有一个问题,没有vtable的类怎么办?内建类型怎么办?其实,没有vtable的类,它不需要多态,它根本就不需要RTTI,内建类型也一样。这就是说,dynamic_cast只支持有虚函数的类。而且, dynamic_cast不能进行non_base_class *到 class T*的转换,比如void * --> class T *,因为它无法去正确获得vtable。

        这样,dynamic_cast的意义和使用方法就很清楚了,它是为了支持多态而存在的。它用于实现从基类到派生类的安全转换。同时它也在绝大多数情况下避免了使用static_cast--不安全的类型转换。

3:结论

       C++ 的RTTI机制虽然简单,或者说简陋,但是它使得静态类型转换变得无用了。这也是C++的一个不可缺少的机制。在未来,如果C++能够提供可选的更强的RTTI机制,就像JAVA里的那样,这种语言可以变得更加强大。当然,到时如何提供不损失性能的 RTTI机制,更是一个值得深入研究的话题了。

 

 

 

 

   对于没有virtual函数的类而言,typeid是编译时期的事情(也就是静态类型);对于有virtual函数的类而言,typeid是运行时期的事情(也就是动态类型)。   
    
     为什么是这样呢?   
     因为RTTI(typeid和dynamic_cast都是RTTI的一部分)大部分都是要利用virtual      table来实现的,比如在virtual      table的第0个索引处存放指向type_info信息的指针。所以如果没有virtual      function,那么此class就没有virtual      table,所以RTTI也自然就不能正常运行了(那么typeid也就自然不能够正常运行了,而只能根据编译时期的静态类型来进行判断)。

抱歉!评论已关闭.