专栏中心

EEPW首页 > 专栏 > 探讨C++的运行时确定对象的类型(RTTI)

探讨C++的运行时确定对象的类型(RTTI)

发布人:华嵌 时间:2013-08-09 来源:工程师 发布文章

  作者:武汉华嵌技术部       姚老师

 

     Runtime Type Information,即运行时确定对象的类型,是C++语言和面向对象理论中的一个重用技术。通常情况下,具有继承树关系的一组对象,通过 UPCAST,交由位于根的基类进行同一处理,这在设计Application Framework的时候,是至关重要的。

    这也带来了两个难以把握的技术难题:

   1是动态绑定,在C++中以虚函数的形式实现。

   2是运行时类型的识别,即RIIT。

   动态绑定的重要性不言而喻,很多书籍文章都在讨论。而RTTI同样重用,若忽视了,则会造成指鹿为马、指狗为猫的错误。

   可以通过下面的C++代码来描述:

#include <iostream>

using namespace std;

 class CAnimal

{

protected:

      char type[32];

};

 class CDog:public CAnimal

{

public:

      CDog()

      {

           strcpy(type, "Dog");

      }

};

 class CCat: public CAnimal

{

public:

      CCat()

      {

           strcpy(type, "Cat");

      }

     void CatchMouse()

      {

           cout << type << " Catch Mouse" << endl;

      }

};

 注意:

1) CatchMouse()方法是猫类独有的

2 )构造函数中,指定了对象的类型。

 接下来,创建一只狗,通过UPCAST到父类CAnimal ,然后强制DOWNCAST转化为猫,这样实现了指狗为猫,并且让狗拿耗子。代码如下:

int main()

{

      CAnimal * pa=new CDog;

      CCat * pc= (CCat * )pa;

      pc->CatchMouse();

      return 0;

}

让人惊奇的是,这段代码不但可以通过编译,还能运行出“狗拿耗子”这样荒谬的结果。

其他的面向对象语言,比如JAVA,决不让这样的程序运行,为什么C++反而可以呢?通过仔细研究发现,C++编译后,成员函数并不包含在对象当中,以下代码可以作证:

int main()

{

      cout << sizeof(CAnimal) << endl;

      cout << sizeof(CCat) << endl;

      cout << sizeof(CDog) << endl;

      return 0;

}

结果是

32

32

32

可见对象只包含数据成员,成员函数游离于对象之外。

 C++是静态语言,在编译时,确定函数的入口地址,因此由于指针的阴差阳错,误调用了猫类的CatchMouse()

 若将猫类的CatchMouse()声明为虚函数,则通过编译,但出现运行错误,代码如下:

 class CCat: public CAnimal

{

public:

      CCat()

      {

           strcpy(type, "Cat");

      }

 virtual void CatchMouse()

      {

           cout << type << " Catch Mouse" << endl;

      }

};

仔细观察,发现猫类对象的大小有所增加,代码和结果如下:

int main()

{

      cout << sizeof(CAnimal) << endl;

      cout << sizeof(CCat) << endl;   //36

      cout << sizeof(CDog) << endl;   //32

 

      return 0;

}

     增加的部分正好是VTable,而在堆内存中创建的是狗对象,尽管经过UPCAST和DOWNCAST被强行指为猫,但对内存中仍然是狗,并没有猫的VTable,因此出现运行错误。

    新版的C++标准,提供了typeid运算符,来实现RTTI功能,代码如下:

 int main()

{

      CAnimal * pa=new CDog;

      CCat * pc= (CCat * )pa;

 

      if(typeid(*(CAnimal *)pc) == typeid(CCat))

      {

           pc->CatchMouse();

      }

      else

      {

           cout << "Is Not a Cat" << endl;

      }

 

      return 0;

}

 注意:

1) 先要转为父类的指针。

2) VC6做以下设置:

Project->Settings->C++Language->Enable RTTI

 但早期的C++编译器,并不支持typeid运算符,因此一些历史悠久的类库,比如MFC,却采用了一些旁门左道的方法:将类别的信息放入链表中,然后一一对比,如图:

 首先准备一个结构体:

 struct CRuntimeClass

{

      // Attributes

      LPCSTR m_lpszClassName;

      int m_nObjectSize;

      UINT m_wSchema; // schema number of the loaded class

      CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class

      CRuntimeClass* m_pBaseClass;

      // CRuntimeClass objects linked together in simple list

      static CRuntimeClass* pFirstClass; // start of class list

      CRuntimeClass* m_pNextClass; // linked list of registered classes

}

然后以静态成员形式,塞入每个类当中:

位于根的基类:

// in header file

class CObject

{

public:

      virtual CRuntimeClass* GetRuntimeClass() const;

      ...

public:

      static CRuntimeClass classCObject;

};

 

// in implementation file

static char szCObject[] = "CObject";

 struct CRuntimeClass CObject::classCObject =

{ szCObject, sizeof(CObject), 0xffff, NULL, NULL };

 static AFX_CLASSINIT _init_CObject(&CObject::classCObject);

 

CRuntimeClass* CObject::GetRuntimeClass() const

{

      return &CObject::classCObject;

}

 以及派生类:

// in header file

class CView : public CWnd

{

public:

      static CRuntimeClass classCView; \

           virtual CRuntimeClass* GetRuntimeClass() const;

      ...

};

 // in implementation file

static char _lpszCView[] = "CView";

 CRuntimeClass CView::classCView = {

      _lpszCView, sizeof(CView), 0xFFFF, NULL,

           &CWnd::classCWnd, NULL };

 

      static AFX_CLASSINIT _init_CView(&CView::classCView);

     CRuntimeClass* CView::GetRuntimeClass() const

      { return &CView::classCView; }

 

于是这样把每个对象的类型追踪出来。

void PrintAllClasses()

{

      CRuntimeClass* pClass;

      // just walk through the simple list of registered classes

      for (pClass = CRuntimeClass::pFirstClass; pClass != NULL;

      pClass = pClass->m_pNextClass)

      {

           cout << pClass->m_lpszClassName << "\n";

           cout << pClass->m_nObjectSize << "\n";

           cout << pClass->m_wSchema << "\n";

      }

}

 (本文为武汉华嵌嵌入式培训所创,http://www.embedhq.org  转载请注明来源)

 

相关链接:

1)  C++双平台就业班

2) 华嵌成功实施邮科院虹信公司Linux下C++企业内训

专栏文章内容及配图由作者撰写发布,仅供工程师学习之用,如有侵权或者其他违规问题,请联系本站处理。 联系我们

关键词:

相关推荐

浅谈多层印制电路板的设计和制作

中国基础设施和运营领导者培养员工生成式人工智能技能的三大举措

中国2025年最大STAR市场首次公开募股Moore Threads于11月24日上市,筹资80亿元人民币

国际视野 2025-11-24

美军APKWS II制导火箭弹技术发展动态

视频 2010-01-14

第二十二届中国国际半导体博览会在京开幕,同期举办第七届全球IC企业家大会

2025-11-24

安森美宣布60亿美元股票回购授权计划

美海军潜艇技术发展新动向

视频 2010-01-14

BOE(京东方)“焕新2026”年终媒体智享会首站落地上海

光电显示 2025-11-21

伴芯科技重磅发布DVcrew与PDcrew两大创新产品,以AI智能体重构EDA

拼版尺寸设计简介

平行导线通过有割缝地层时的影响的时域分析图形

拼版尺寸设计简介(1)

美军混合电驱动车辆发展近况

视频 2010-01-14

美陆军调整陆航采办计划

视频 2010-01-14

全球电子协会发布《双重重要性评估(DMA)工具包》 助力电子产业合规开展可持续发展报告

浅谈电视机各式保护电路

美空军计划未来10年采购2000架无人机

视频 2010-01-14

【泰克 × 零碳未来】系列之二:学生车队凭借泰克测试设备取得领先进展

iDEAL Semiconductor以SuperQ™ MOSFET为高压电池设定新安全标准

功率二极管与整流技术解析:结构特性、工作机理与半波整流实现

EDA/PCB 2025-11-24
更多 培训课堂
更多 焦点
更多 视频

技术专区