RTTI运行时类型识别与类对象的动态创建_第1页
RTTI运行时类型识别与类对象的动态创建_第2页
RTTI运行时类型识别与类对象的动态创建_第3页
RTTI运行时类型识别与类对象的动态创建_第4页
RTTI运行时类型识别与类对象的动态创建_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

RTTI 运行时类型识别 CRuntimeClass 是 MFC 专用的 CRuntimeClass 在文件 AFX H 中声明 它是用来串起 MFC 从 COBJECT 继承下来的所有类 也可以把自己写的类加入这个链表 struct CRuntimeClass Attributes LPCSTR m lpszClassName 类名 int m nObjectSize 类对象大小 UINT m wSchema 分类编号 对不可分类的类 该值为 1 CObject PASCAL m pfnCreateObject NULL abstract class CRuntimeClass m pBaseClass 基类指针 但这里指针一定是指向父亲的 而不会指向祖 父 CRuntimeClass objects linked together in simple list static CRuntimeClass pFirstClass class list 的链表头 注意这个与基类指针不同 并且 该对象是个静态变量 所有该对象是个静态变量 所有 CRuntimeClass 对象共享对象共享 CRuntimeClass m pNextClass 链表中紧跟当前对象的下一个对象 与当前对象不一定有 继承关系 特别注意 该 struct 使用了链表的概念 但是该链表与常规的数据结构链表是不太一 样的 该链表每次新加入的节点都是放在链表头上的 类似栈 所以 pFirstClass 是随着每 次新节点的加入一直在更新的 pFirstClass 必然指向新加入的节点 而 m pNextClass 则指 向前一个加入的节点 如下图 首先初始化 static CRuntimeClass pFirstClass 为 NULL 最开始加入链表的是 CObject 此时 在 IMPLEMENT RUNTIMECLASS 宏中先直接设置 m pBaseClass m pBaseClass NULL 然后在 IMPLEMENT RUNTIMECLASS 宏中调用 AFX CLASSINIT 的构造函数设置 m pNextClass 与 pFirstClass 这两者是先 m pNextClass 后 pFirstClass m pNextClass pFirstClass NULL pFirstClass CObject 然后 CCmdTarget 加入链表 数据更新顺序同上 m pBaseClass CObject m pNextClass pFirstClass CObject pFirstClass CCmdTarget 接着 CWinThread 加入链表 数据更新顺序同上 m pBaseClass CCmdTarget m pNextClass pFirstClass CCmdTarget pFirstClass CWinThread 每一个类都拥有这样一个 static 的 CRuntimeClass 成员变量 由于每个类都有 static 的 CRuntimeClass 成员变量 所以每个类的对象都是引用以及修改本类的 static 的 CRuntimeClass 成员变量 而不会将父类或者子类的修改掉 父类与子类中的 static 同名 成员变量是会造成 覆盖 的 该覆盖指的是引用时若不加域名直接使用 则使用的是本 类的 而不会引用父类的 但实际上在内存中父类的 static 同名成员变量是依然存在的 每个 static 的 CRuntimeClass 成员变量都有一定的命名规则 在 CRuntimeClass 中采用 的方法是在每个类的类名之前冠以 class 作为它的名称 如 CView 的名称为 classCView 然后 经由某种手段将整个类别库构造好之后 类别型录 能呈现类似这样的风貌 注意是每个类共享一个 CRuntimeClass 成员变量 例 CCmdTarget cmd1 cmd2 CWinThread thread 上面两个类定义了三个对象 其中 cmd1 与 cmd2 共享一个 CRuntimeClass 成员变量 所以 cmd1 与 cmd2 所拥有的 CRuntimeClass 成员变量是同一个 thread 单独使用一个 CRuntimeClass 成员变量 thread 所使用的这个 CRuntimeClass 成员变量与 cmd1 cmd2 共享的那个 CRuntimeClass 成员变量不是同一个 pFirstClass 是 CRuntimeClass 结构体中的 static 变量 所以所有的对象都共享 pFirstClass 因此 cmd1 cmd2 thread 三者共享 CRuntimeClass 结构体中的 static 变量 pFirstClass 对于 CView 其 CView h class CView public CWnd DECLARE DYNAMIC CView CView cpp IMPLEMENT DYNAMIC CsdiTestView CView 其中的两个宏 DECLARE DYNAMIC 与 IMPLEMENT DYNAMIC 展开后 代码如下 define DECLARE DYNAMIC class name public static CRuntimeClass class class name virtual CRuntimeClass GetRuntimeClass const define IMPLEMENT DYNAMIC class name base class name IMPLEMENT RUNTIMECLASS class name base class name 0 xFFFF NULL 出现在宏定义之中的 用来告诉编译器 把两个字符串系在一起 如果你这么使用 此宏 DECLARE DYNAMIC CView 编译器前置处理器为你做出的码是 public static CRuntimeClass classCView 静态成员变量 所有该类对象都共享该变量 virtual CRuntimeClass GetRuntimeClass const IMPLEMENT DYNAMIC 宏之中又使用了一个 IMPLEMENT RUNTIMECLASS 宏 之所以这样做是因为 IMPLEMENT RUNTIMECLASS 宏在动态创建时还要用到 IMPLEMENT RUNTIMECLASS 宏定义 define IMPLEMENT RUNTIMECLASS class name base class name wSchema pfnNew static char lpsz class name class name CRuntimeClass class name class class name lpsz class name sizeof class name wSchema pfnNew RUNTIME CLASS base class name NULL static AFX CLASSINIT init class name CRuntimeClass class name GetRuntimeClass const return 其中又有 RUNTIME CLASS 宏 该宏用于取指定类的静态 CRuntimeClass 对象地址 由于在上面代码中调用该宏是 RUNTIME CLASS base class name NULL 所以传入 的参数是 base class name 从而获取的地址是基类 即父类 的静态 CRuntimeClass 对 象地址 其定义如下 define RUNTIME CLASS class name 这表示它有一个构造函数 别惊讶 C 的 struct 与 class 都有构造式 定义如下 AFX CLASSINIT AFX CLASSINIT CRuntimeClass pNewClass pNewClass m pNextClass CRuntimeClass pFirstClass CRuntimeClass pFirstClass pNewClass 很明显 此构造式负责 linked list 的串接工作 即将所有对象 in header file class CView public CWnd DECLARE DYNAMIC CView in implementation file IMPLEMENT DYNAMIC CView CWnd 上述的码展开来成为 头文件 class CView public CWnd public static CRuntimeClass classCView virtual CRuntimeClass GetRuntimeClass const 展开后定义了一个 CRuntimeClass 静态对象以及该对象的获取函数 静态成员变量静态成员变量 classCView 所有该类对象都共享该变量 所有该类对象都共享该变量 获取函数在这里仅仅是声明 具体实现需要在 CPP 中进行 实现 static char lpszCView CView CRuntimeClass 的第一个成员变量 注意这里定义了一个 static 的字符串 下面传参传入的就是这个字符串 这样做会使得所 有的该类成员名称都相同 CRuntimeClass CView classCView lpszCView sizeof CView 0 xFFFF NULL 该行代码用于为定义的 CRuntimeClass 对象 classCView 赋值 因为 CRuntimeClass 是个结 构体 所以可以用 将所有的值包含在内并直接使用 来进行赋值 注意这种赋值方式仅仅会为结构体的常规成员变量赋值 而 static 变量与函数都是单独存 储在另一片内存区的 所以不会被赋值 static AFX CLASSINIT init CView CRuntimeClass CView GetRuntimeClass const return 对 CRuntimeClass 对象的获取函数进行实现 所有的 MFC 类 只要在类内调用上面的两个宏 DECLARE DYNAMIC 与 IMPLEMENT DYNAMIC 即可将 CRuntimeClass 对象及其相关函数塞入类中 从而可以 实现运行时类型识别的功能 DECLARE DYNAMIC 宏必须配合 IMPLEMENT DYNAMIC 宏来使用 而 IMPLEMENT DYNAMIC 宏又必须传入当前类的基类 MFC 所有的类都是从 CObject 派 生而来 所以除 CObject 之外的类调用这两个宏是没问题的 但 CObject 是没有基类的 所以 CObject 直接调用这两个宏会有问题 于是 CObject 就不再调用这两个宏 而是改为单独进行 CRuntimeClass 对象及其相关 函数的实现 直接将代码写在了 CObject 类的 h 文件中 同时 由于 CRuntimeClass 内部的 pFirstClass 变量是 static 类型 CObject 是第一个使 用 CRuntimeClass 对象的类 所以在 CObject 所在的头文件需要对 pFirstClass 进行初值的 赋值 MFC 中将其赋值为 NULL 编译过程中 比如定义了一个 CWinApp 对象 一个 CFrameWnd 对象 一个 CDocument 对象 一 个 CView 对象 首先编译 CWinApp 对象 检查 CWinApp 对象是否有基类 若有 则检查其基类 是否编译过 若没有编译过 则先编译其基类 然后对其基类进行检测 检测其是否 有基类 不断重复上述步骤 直到检测到 CObject 为止 于是 就出现了这样的编译顺序 CObject CCmdTarget CWinThread CWinApp 然后编译 CFrameWnd 对象 检测 CFrameWnd 的基类 发现是 CWnd 没有编译 过 检测 CWnd 的基类 发现是 CCmdTarget CCmdTarget 已经编译过了 故而不再 编译 于是其编译顺序为 CWnd CFrameWnd 接着编译 CDocument 对象 其基类是 CCmdTarget 已经编译过了 故而编译顺 序为 CDocument 最后编译 CView 对象 其基类是 CWnd 已经编译过了 故而编译顺序为 CView 如下图 有了上面这张网 就可以实现 RTTI 的能力 由于 CObject 是所有类的基类 因此在 CObject 中定义一个函数 IsKindOf 以便于所 有类继承 该函数只需要在 CObject 中定义 BOOL CObject IsKindof const CRuntimeClass pClass const CRuntimeClass pClassThis GetRuntimeClass while pClassThis NULL if pClassThis pClass return TRUE pClassThis pClassThis m pBaseClass return FALSE 当对某个类调用了 IsKindOf 函数时 IsKindOf 函数就会判断当前类的 CRuntimeClass 对象与参数指定类的 CRuntimeClass 对象是否相等 若相等 说明当前类就是指定类 也就是当前类与指定类是同一个类 返回 TRUE 若不相等 那就判断当前类的基类 CRuntimeClass 对象与指定类的 CRuntimeClass 对象是否相等 若相等 说明当前类的基类与指定类是同一个类 从而当前类是指定类的 派生类 返回 TRUE 若依然不相等 继续重复步骤 如果遍历到 CObject 依然与指定类的 CRuntimeClass 对象不相等 那么说明当前 类既不是指定类也不是指定类的派生类 返回 FALSE 使用方法 如有 1 个对象 CMyDoc pMyDoc 则 判断 pMyDoc 是否属于 CMyDoc 返回 TRUE pMyDoc IsKindOf RUNTIME CLASS CMyDoc 判断 pMyDoc 是否属于 CObject 返回 TRUE pMyDoc IsKindOf RUNTIME CLASS CObject 判断 pMyDoc 是否属于 CView 返回 FALSE pMyDoc IsKindOf RUNTIME CLASS CView 以上是为了实现在运行时可以识别某个类型 要做到动态创建 还需要在 CRuntimeClass 的 struct 中加入 struct CRuntimeClass CObject CreateObject static CRuntimeClass PASCAL Load 两个函数的实现 CObject CRuntimeClass CreateObject 该函数用于创建一个当前类的对象 if m pfnCreateObject NULL return NULL CObject pObject pObject m pfnCreateObject 函数指针调用 return pObject CRuntimeClass PASCAL CRuntimeClass Load 该函数用于匹配输入的类名 并返回一个 指向该类的 CRuntimeClass 指针 否则返回 NULL char szClassXXX 64 CRuntimeClass pClass cin szClassXXX 假定这是我们希望动态创建的类名 这里用手动输入 for pClass pFirstClass pClass NULL pClass pClass m pNextClass if strcmp szClassXXX pClass m lpszClassName 0 return pClass return NULL 同时要添加两个宏 DECLARE DYNCREATE 与 IMPLEMENT DYNCREATE define DECLARE DYNCREATE class name DECLARE DYNAMIC class name static CObject PASCAL CreateObject define IMPLEMENT DYNCREATE class name base class name CObject PASCAL class name CreateObject return new class name IMPLEMENT RUNTIM

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论