C++RTTI小总结

RTTI算是C++的一大特性之一了,但也是一个不太好懂的特性。以前一直没有理解清楚RTTI。通过最近的学习,似乎掌握了一点皮毛,这里做一个小总结。首先是《C++编程思想》上的一个例子,由于缺头文件,我把容器改成了vector。

#include <iostream>
#include <time.h>
#include <typeinfo.h>
#include <vector>
#include <stdlib.h>  //for srand() and rand()
using namespace std;

class shape {
protected:
    static int count;
public:
    shape() { count++; }
    virtual ~shape() { count--; }
    virtual void draw() const = 0;
    static int quantity() {return count;}
};

int shape::count = 0;

class rectangle:public shape {
    void operator=(rectangle&);
protected:
    static int count;
public:
    rectangle() {count++;}
    ~rectangle() {count--;}
    rectangle(const rectangle&) {count++;}
    void draw() const {
        cout << "rectangle::draw()"<<endl;
    }
    static int quantity() {return count;}

};

int rectangle::count = 0;

class ellipse: public shape {
    void operator=(ellipse&);
protected:
    static int count;
public:
    ellipse() {count++;}
    ellipse(const ellipse&) {count++;}
    ~ellipse() { count--;}
    void draw() const{
        cout << "ellipse::draw()" <<endl;
    }
    static int quantity() {return count;}
};

int ellipse::count = 0;

class circle : public shape {
    void operator=(circle&);
protected:
    static int count;
public:
    circle() {count++;}
    circle(const circle&) {count++;}
    ~circle() {count--;}
    void draw() const {
        cout << "circle::draw()" << endl;
    }
    static int quanlity() {return count;}
};
int circle::count = 0;

int main()
{
    vector<shape*> shapes;
    time_t t;
    srand((unsigned)time(&t));
    const int mod = 12;
    for(int i = 0;i<rand()%mod; i++)
        shapes.push_back(new rectangle);
    for(int j = 0;j<rand() %mod; j++)
        shapes.push_back(new ellipse);
    for(int k = 0;k<rand() %mod; k++)
        shapes.push_back(new circle);
    int Ncircles = 0;
    int Nellipses = 0;
    int Nrects = 0;
    int Nshapes = 0;
    for(int u = 0;u<shapes.size();u++) {
        shapes[u]->draw();
        if(dynamic_cast<circle*>(shapes[u]))
                Ncircles++;
        if(dynamic_cast<ellipse*>(shapes[u]))
                Nellipses++;
        if(dynamic_cast<rectangle*>(shapes[u]))
                Nrects++;
        if(dynamic_cast<shape*>(shapes[u]))
                Nshapes++;

    }
    cout << endl <<endl
         << "circles = " << Ncircles <<endl
         << "ellipses = " << Nellipses <<endl
         << "rectangles = " << Nrects <<endl
         << "shapes = " << Nshapes <<endl
         << endl
         << "circle::quantity() = "
         << circle::quanlity() <<endl
         << "ellipses::quantity() = "
         << ellipse::quantity() << endl
         << "rectangle::quantity() = "
         << rectangle::quantity() << endl
         << "shape::quantity() = "
         << shape::quantity() << endl;
    shapes.clear();
}

下面总结一下几个知识要点,条理不一定清楚;

(1)、指针是另一种类型;

int和int*是不同的,比如shape* s = new shape,其typeid(s) == typeid(shape*);而typeid(shape) == typeid(*s)。

(2)、引用等同于被引用类型;

这句话说起来非常拗口,但是道理就是这样。本质上引用是阉割版的指针,但是在C++语言层面上还是有很大区别的,它是一个独立的类型,它是和类型实例绑定在一起的。下面这个例子可以同时解释第一条。

class B {
public:
	virtual float f() {return 1.0;}
};
class D : public B {
};

B* p = new D;
B& r = *p;

typeid()看到的指针类型是基类而不是派生类,这证明了第一条结论,此时的p是一个B型的指针类型,typeid()不会去管指针实际指向的目标。typeid()它看到的引用类型则是派生类,*p是使用p进行了一次指针运算操作,然后r就绑定到了一个D型的实例,也就是说引用的关注点在对象上。

typeid(p) == typeid(B*)

typeid(p) != typeid(D*)

typeid(r) == typeid(D)

与此相反,指针指向的类型在typeid()看来是派生类而不是基类,而用一个引用的地址时产生的是基类而不是派生类。这是因为*p依然是一次指针运算操作,它指向了一个对象,而对引用取地址得到的是一个指针类型,相当于对类型实例做了一次取地址运算。

typeid(*p) == typeid(D)

typeid(*p) != typeid(B)

typeid(&r) == typeid(B*)

typeid(&r) != typeid(D*)

(3)、RTTI一般用于多态类型;

多态类型指的是基类中没有虚函数的类型。典型的RTTI是通过在 VTABLE中放一个额外的指针来实现的。这个指针指向一个描述该特定类型的 typeinfo结构(每个新类只产生一个typeinfo的实例),所以typeid()表达式的作用实际上很简单。VPTR用来取typeinfo的指针,然后产生一个结果typeinfo结构的一个引用。dynamic_cast()的过程稍微复杂点,但也需要利用VTABLE中的信息来进行类型转换。

我不知道怎么去访问到VTABLE的typeinfo信息,倒是在找到了一个访问VTABLE中函数信息的例子。这个例子告诉我们VTABLE不止在书上,感兴趣的可以运行一下,或者改改,有可能能找到typeinfo哟!

#include <iostream>

using namespace std;

class Base
{
private:
    int a;
public:
    virtual void fun1()
    {
        cout<<"Base::fun1()"<<endl;
    }

    virtual void fun2()
    {
        cout<<"Base::fun2()"<<endl;
    }

    virtual void fun3()
    {
        cout<<"Base::fun3()"<<endl;
    }

};

class A:public Base
{
private:
    int a;
public:
    void fun1() {
        cout<<"A::fun1()"<<endl;
    }
    void fun2() {
        cout<<"A::fun2()"<<endl;
    }

};
void foo(Base& obj)
{
    obj.fun1();
    obj.fun2();
    obj.fun3();
}

//int main()
//{
//    Base b;
//    A a;
//    foo(b);
//    foo(a);
//}
void *getp(void* p)
{
    return (void*)*(unsigned long *)p;
}
typedef void (*fun)();

fun getfun(Base* obj, unsigned long off)
{
    void *vptr = getp(obj);
    unsigned char *p = (unsigned char *)vptr;
    p += sizeof(void *) * off;
    cout<<(int)p<<endl;
    return (fun)getp(p);
}

int main()
{
    Base *p = new A;
    fun f = getfun(p,0);
    (*f)();
    f = getfun(p,1);
    (*f)();
    f = getfun(p,2);
    (*f)();
    delete p;
}
时间: 2024-12-15 07:09:16

C++RTTI小总结的相关文章

C++的 RTTI 观念和用途(非常详细)

自从1993年Bjarne Stroustrup [注1 ]提出有关C++ 的RTTI功能之建议﹐以及C++的异常处理(exception handling)需要RTTI:最近新推出的C++ 或多或少已提供RTTI. 然而,若不小心使用RTTI,可能会导致软件弹性的降低.本文将介绍RTTI的观念和近况﹐并说明如何善用它. 什么是RTTI﹖      在C++ 环境中﹐头文件(header file) 含有类之定义(class definition)亦即包含有关类的结构资料(representat

Java多态小总结

多态,又可以称为动态绑定,即在运行时确定类型,比如: 1 class A { 2 void draw(){ 3 //输出“A” 4 } 5 } 6 class B { 7 void draw(){ 8 //输出“B” 9 } 10 11 } 这种关系里,如果调用A a = new B(); 此时,被称为向上转型,a的类型可能在很早之前被生命,而在这时候被明确指明是其子类型, 我们如果要去调用draw()方法的时候,会调用输出“B”,这样,便是Java中的“多态”.我们称其为“向上转型”. 但是,

用DELPHI的RTTI实现对象的XML持久化

去年我花了很多时间尝试用DELPHI进行基于XML的WEB应用开发.起初的设想是很美好的,但结果做出来的东西很简陋.一部分原因就在于XML到Object之间的数据绑定实现太麻烦(另一部分是因为对XSLT不熟,学习它花了很多时间). 之前我一直是用DELPHI提供的XML Data binding来做的,基本做法是:先用工具(如XMLSPY)做好一个XML Schema(XSD),然后用XML Data binding生成DELPHI的接口和类.当然,一旦生成好就很方便了,在程序里我只要操作这个接

C++/C#小知识:关于try_catch 详解

概述:try { //执行的代码,其中可能有异常.一旦发现异常,则立即跳到catch执行.否则不会执行catch里面的内容 } catch { //除非try里面执行代码发生了异常,否则这里的代码不会执行 } finally { //不管什么情况都会执行,包括try catch 里面用了return ,可以理解为只要执行了try或者catch,就一定会执行 finally } 详解: 一.简单的例子 首先通过一个简单的例子来熟悉C++ 的 try/catch/throw: 1    #inclu

减小Delphi2010程序的尺寸(关闭RTTI反射机制)

自从Delphi2010增强了RTTI反射机制后,编译出来的程序变得更大了,这是因为默认情况下 Delphi2010 给所有类都加上了RTTI信息(呵呵,不管你用不用它,好像实用价值确实不高,至少目前我不会去用的).虽说对程序的运行速度影响不大,但会增加安装程序的大小,这也是我们不愿看到的.有没有办法禁用RTTI信息,从而减小编译后程序的大小呢?,从文档中我们找到了方法. 一.在工程中用编译指令禁用RTTI 禁用的方法很简单,就是要在工程(dpr文件中.Delphi2010下项目文件是dproj

Delphi里的RTTI与反射(举例换掉FOnChange)

Delphi2010之后的RTTI做了很大休整,现在用起来很爽了哦.甚至可以获取某些类的内部私有单元,然后为其赋值!讲这个RTTI增强的,可以参考网上的多个博客内容,我列举一下:Delphi2010RTTI的增强Delphi的Anymouse方法探秘万一的Rtti系列 我这里的主要目的是挂钩某些内部私有事件,然后增加上一些自己的处理过程,这里我以TMenuItem的私有内部事件FOnChange作为例程.这个私有事件在菜单内部绑定,我们平常状态下,在外部无法更改!但是XE之后这个问题不在存在,使

MFC六大核心机制之二:运行时类型识别(RTTI)

上一节讲的是MFC六大核心机制之一:MFC程序的初始化,本节继续讲解MFC六大核心机制之二:运行时类型识别(RTTI). typeid运算子 运行时类型识别(RTTI)即是程序执行过程中知道某个对象属于某个类,我们平时用C++编程接触的RTTI一般是编译器的RTTI,即是在新版本的VC++编译器里面选用“使能RTTI”,然后载入typeinfo.h文件,就可以使用一个叫typeid()的运算子,它的地位与在C++编程中的sizeof()运算子类似的地方(包含一个头文件,然后就有一个熟悉好用的函数

RTTI (Run-Time Type Identification,通过运行时类型识别) 转

参考一: RTTI(Run-Time Type Identification,通过运行时类型识别)程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型. RTTI提供了以下两个非常有用的操作符: (1)typeid操作符,返回指针和引用所指的实际类型: (2)dynamic_cast操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用. 面向对象的编程语言,象C++,Java,delphi都提供了对RTTI的支持. 本文将简略介绍 RTTI 的一些背景知识.描述 R

C++中的RTTI(转)

转自:http://blog.csdn.net/mannhello/article/details/5217954 RTTI 是“Runtime Type Information”的缩写,意思是:运行时类型信息.它提供了运行时确定对象类型的方法.本文将简略介绍 RTTI 的一些背景知识.描述 RTTI 的概念,并通过具体例子和代码介绍什么时候使用以及如何使用 RTTI:本文还将详细描述两个重要的 RTTI 运算符的使用方法,它们是 typeid 和 dynamic_cast. 其实,RTTI 在