【vc】CObject类

  1.  

  2. CObject类

    CObject是大多数MFC类的根类或基类。CObject类有很多有用的特性:对运行时类信息的支持,对动态创建的支持,对串行化的支持,对象诊断输出,等等。MFC从CObject派生出许多类,具备其中的一个或者多个特性。程序员也可以从CObject类派生出自己的类,利用CObject类的这些特性。

    本章将讨论MFC如何设计CObject类的这些特性。首先,考察CObject类的定义,分析其结构和方法(成员变量和成员函数)对CObject特性的支持。然后,讨论CObject特性及其实现机制。

    1.  

    2. CObject的结构

      以下是CObject类的定义:

      class CObject

      {

      public:

       

      //与动态创建相关的函数

      virtual CRuntimeClass* GetRuntimeClass() const;

      析构函数

      virtual ~CObject(); // virtual destructors are necessary

       

      //与构造函数相关的内存分配函数,可以用于DEBUG下输出诊断信息

      void* PASCAL operator new(size_t nSize);

      void* PASCAL operator new(size_t, void* p);

      void PASCAL operator delete(void* p);

      #if defined(_DEBUG) && !defined(_AFX_NO_DEBUG_CRT)

      void* PASCAL operator new(size_t nSize, LPCSTR lpszFileName, int nLine);

      #endif

       

      //缺省情况下,复制构造函数和赋值构造函数是不可用的

      //如果程序员通过传值或者赋值来传递对象,将得到一个编译错误

      protected:

      //缺省构造函数

      CObject();

      private:

      //复制构造函数,私有

      CObject(const CObject& objectSrc); // no implementation

      //赋值构造函数,私有

      void operator=(const CObject& objectSrc); // no implementation

       

      // Attributes

      public:

      //与运行时类信息、串行化相关的函数

      BOOL IsSerializable() const;

      BOOL IsKindOf(const CRuntimeClass* pClass) const;

      // Overridables

      virtual void Serialize(CArchive& ar);

      // 诊断函数

      virtual void AssertValid() const;

      virtual void Dump(CDumpContext& dc) const;

       

      // Implementation

      public:

      //与动态创建对象相关的函数

      static const AFX_DATA CRuntimeClass classCObject;

      #ifdef _AFXDLL

      static CRuntimeClass* PASCAL _GetBaseClass();

      #endif

      };

       

      由上可以看出,CObject定义了一个CRuntimeClass类型的静态成员变量:

      CRuntimeClass classCObject

      还定义了几组函数:

      构造函数析构函数类,

      诊断函数,

      与运行时类信息相关的函数,

      与串行化相关的函数。

      其中,一个静态函数:_GetBaseClass;五个虚拟函数:析构函数、GetRuntimeClass、Serialize、AssertValid、Dump。这些虚拟函数,在CObject的派生类中应该有更具体的实现。必要的话,派生类实现它们时可能要求先调用基类的实现,例如Serialize和Dump就要求这样。

      静态成员变量classCObject和相关函数实现了对CObjet特性的支持。

       

    3. CObject类的特性

下面,对三种特性分别描述,并说明程序员在派生类中支持这些特性的方法。

  1.  

  2. 对运行时类信息的支持

该特性用于在运行时确定一个对象是否属于一特定类(是该类的实例),或者从一个特定类派生来的。CObject提供IsKindOf函数来实现这个功能。

从CObject派生的类要具有这样的特性,需要:

  •  

  • 定义该类时,在类说明中使用DECLARE_DYNAMIC(CLASSNMAE)宏;

     

  • 在类的实现文件中使用IMPLEMENT_DYNAMIC(CLASSNAME,BASECLASS)宏。

 

  1.  

  2. 对动态创建的支持

前面提到了动态创建的概念,就是运行时创建指定类的实例。在MFC中大量使用,如前所述框架窗口对象、视对象,还有文档对象都需要由文档模板类(CDocTemplate)对象来动态的创建。

从CObject派生的类要具有动态创建的功能,需要:

  •  

  • 定义该类时,在类说明中使用DECLARE_DYNCREATE(CLASSNMAE)宏;

     

  • 定义一个不带参数的构造函数(默认构造函数);

     

  • 在类的实现文件中使用IMPLEMENT_DYNCREATE(CLASSNAME,BASECLASS)宏;

     

  • 使用时先通过宏RUNTIME_CLASS得到类的RunTime信息,然后使用CRuntimeClass的成员函数CreateObject创建一个该类的实例。

例如:

CRuntimeClass* pRuntimeClass = RUNTIME_CLASS(CNname)

//CName必须有一个缺省构造函数

CObject* pObject = pRuntimeClass->CreateObject();

//用IsKindOf检测是否是CName类的实例

Assert( pObject->IsKindOf(RUNTIME_CLASS(CName));

 

  1.  

  2. 对序列化的支持

“序列化”就是把对象内容存入一个文件或从一个文件中读取对象内容的过程。从CObject派生的类要具有序列化的功能,需要:

  •  

  • 定义该类时,在类说明中使用DECLARE_SERIAL(CLASSNMAE)宏;

     

  • 定义一个不带参数的构造函数(默认构造函数);

     

  • 在类的实现文件中使用IMPLEMENT_SERIAL(CLASSNAME,BASECLASS)宏;

     

  • 覆盖Serialize成员函数。(如果直接调用Serialize函数进行序列化读写,可以省略前面三步。)

 

对运行时类信息的支持、动态创建的支持、串行化的支持层(不包括直接调用Serailize实现序列化),这三种功能的层次依次升高。如果对后面的功能支持,必定对前面的功能支持。支持动态创建的话,必定支持运行时类信息;支持序列化,必定支持前面的两个功能,因为它们的声明和实现都是后者包含前者。

  1.  

  2. 综合示例:

定义一个支持串行化的类CPerson:

class CPerson : public CObject

{

public:

DECLARE_SERIAL( CPerson )

// 缺省构造函数

CPerson(){}{};

 

CString m_name;

WORD m_number;

 

void Serialize( CArchive& archive );

 

// rest of class declaration

};

 

实现该类的成员函数Serialize,覆盖CObject的该函数:

void CPerson::Serialize( CArchive& archive )

{

// 先调用基类函数的实现

CObject::Serialize( archive );

 

// now do the stuff for our specific class

if( archive.IsStoring() )

archive << m_name << m_number;

else

archive >> m_name >> m_number;

}

 

使用运行时类信息:

CPerson a;

ASSERT( a.IsKindOf( RUNTIME_CLASS( CPerson ) ) );

ASSERT( a.IsKindOf( RUNTIME_CLASS( CObject ) ) );

动态创建:

CRuntimeClass* pRuntimeClass = RUNTIME_CLASS(CPerson)

//Cperson有一个缺省构造函数

CObject* pObject = pRuntimeClass->CreateObject();

Assert( pObject->IsKindOf(RUNTIME_CLASS(CPerson));

  1.  

  2. 实现CObject特性的机制

    由上,清楚了CObject的结构,也清楚了从CObject派生新类时程序员使用CObject特性的方法。现在来考察这些方法如何利用CObjet的结构,CObject结构如何支持这些方法。

    首先,要揭示DECLARE_DYNAMIC等宏的内容,然后,分析这些宏的作用。

    1.  

    2. DECLARE_DYNAMIC等宏的定义

MFC提供了DECLARE_DYNAMIC、DECLARE_DYNCREATE、DECLARE_SERIAL声明宏的两种定义,分别用于静态链接到MFC DLL和动态链接到MFC DLL。对应的实现宏IMPLEMNET_XXXX也有两种定义,但是,这里实现宏就不列举了。

 

MFC对这些宏的定义如下:

#ifdef _AFXDLL //动态链接到MFC DLL

#define DECLARE_DYNAMIC(class_name) /

protected: /

static CRuntimeClass* PASCAL _GetBaseClass(); /

public: /

static const AFX_DATA CRuntimeClass class##class_name; /

virtual CRuntimeClass* GetRuntimeClass() const; /

 

#define _DECLARE_DYNAMIC(class_name) /

protected: /

static CRuntimeClass* PASCAL _GetBaseClass(); /

public: /

static AFX_DATA CRuntimeClass class##class_name; /

virtual CRuntimeClass* GetRuntimeClass() const; /

 

#else

#define DECLARE_DYNAMIC(class_name) /

public: /

static const AFX_DATA CRuntimeClass class##class_name; /

virtual CRuntimeClass* GetRuntimeClass() const; /

 

#define _DECLARE_DYNAMIC(class_name) /

public: /

static AFX_DATA CRuntimeClass class##class_name; /

virtual CRuntimeClass* GetRuntimeClass() const; /

 

#endif

 

// not serializable, but dynamically constructable

#define DECLARE_DYNCREATE(class_name) /

DECLARE_DYNAMIC(class_name) /

static CObject* PASCAL CreateObject();

 

#define DECLARE_SERIAL(class_name) /

_DECLARE_DYNCREATE(class_name) /

friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb);

 

由于这些声明宏都是在CObect派生类的定义中被使用的,所以从这些宏的上述定义中可以看出,DECLARE_DYNAMIC宏给所在类添加了一个CRuntimeClass类型的静态数据成员class##class_name(类名加前缀class,例如,若类名是CPerson,则该变量名称是classCPerson),且指定为const;两个(使用MFC DLL时,否则,一个)成员函数:虚拟函数GetRuntimeClass和静态函数_GetBaseClass(使用MFC DLL时)。

DECLARE_DYNCREATE宏包含了DECLARE_DYNAMIC,在此基础上,还定义了一个静态成员函数CreateObject。

DECLARE_SERIAL宏则包含了_DECLARE_DYNCREATE,并重载了操作符“>>”(友员函数)。它和前两个宏有所不同的是CRuntimeClass数据成员class##class_name没有被指定为const。

 

对应地,MFC使用三个宏初始化DECLARE宏所定义的静态变量并实现DECLARE宏所声明的函数:IMPLEMNET_DYNAMIC,IMPLEMNET_DYNCREATE,IMPLEMENT_SERIAL。

首先,这三个宏初始化CRuntimeClass类型的静态成员变量class#class_name。IMPLEMENT_SERIAL不同于其他两个宏,没有指定该变量为const。初始化内容在下节讨论CRuntimeClass时给出。

其次,它实现了DECLARE宏声明的成员函数:

  •  

  • _GetBaseClass()

返回基类的运行时类信息,即基类的CRuntimeClass类型的静态成员变量。这是静态成员函数。

  •  

  • GetRuntimeClass()

返回类自己的运行类信息,即其CRuntimeClass类型的静态成员变量。这是虚拟成员函数。

对于动态创建宏,还有一个静态成员函数CreateObject,它使用C++操作符和类的缺省构造函数创建本类的一个动态对象。

  •  

  • 操作符的重载

对于序列化的实现宏IMPLEMENT_SERIAL,还重载了操作符<<和定义了一个静态成员变量

static const AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name));

比如,对CPerson来说,该变量是_init_Cperson,其目的在于静态成员在应用程序启动之前被初始化,使得AFX_CLASSINIT类的构造函数被调用,从而通过AFX_CLASSINIT类的构造函数在模块状态的CRuntimeClass链表中插入构造函数参数表示的CRuntimeClass类信息。至于模块状态,在后文有详细的讨论。

重载的操作符函数用来在序列化时从文档中读入该类对象的内容,是一个友员函数。定义如下:

CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb)

{

pOb = (class_name*) ar.ReadObject(

RUNTIME_CLASS(class_name));

return ar;

}

回顾CObject的定义,它也有一个CRuntimeClass类型的静态成员变量classCObject,因为它本身也支持三个特性。

以CObject及其派生类的静态成员变量classCObject为基础,IsKindOf和动态创建等函数才可以起到作用。

这个变量为什么能有这样的用处,这就要分析CRuntimeClass类型变量的结构和内容了。下面,在讨论了CRuntimeClass的结构之后,考察该类型的静态变量被不同的宏初始化之后的内容。

 

 



你可能感兴趣的:(windows,pascal,class,mfc,archive,dll,attributes)