关于OOP中若干特性的思考

(原文写于2010-11-6,发表在振动论坛,算法及编程语言分区)
(重编辑于2015-11-13,在vs2010平台上重新运行了这些C++代码,依然有效)

由于最近在学习Python,了解了很多所谓Python的新特性,无非是更加方便和精确地实现了面对对象(OOP)编程的精髓。有些网友甚至急匆匆地就拿Python与各种语言作比较了,我想说的是,并非C++不能这样做,是因为它不必这样做(关于C++的四种编程层面,请参考《Effective C++》,面对对象编程,只是其中的一种)。

要想用C++实现这些现代面对对象编程语言的典范功能,并不需要改进语法,只需要精妙地构建一些类和宏就可以了,关于这项工作的典范,是微软出品的MFC(Microsoft Fundation Class)系列库类,设计这些库类的出发点,是将面对对象的观点引入基于windows的应用程序设计上,其亮点在于这是一个完全构建在C++之上的库类,并没有修改C++语法,实现了WINDOWS下面对对象编程所谓的“六大关键技术”,分别是:

  类层级结构(对应于Python的“万物皆对象”思想,所有的MFC类都有共同的基类:CObject类);
  运行时类型识别(对应于Python的type()函数,接受任何东西作为参数,返回的字符串告诉你这个东西是什么类别);
  动态创建(相当于Python的cPickle库或者Pickle库的load()函数,从文件中还原类的实例
  永久保存(相当于Python的cPickle库或者Pickle库的dump()函数,将类的实例保存到文件);
  消息映射(操作系统相关的技术);
  命令传递(操作系统相关的技术);

当年在研习候俊杰的《深入浅出MFC》时,照着书上的讲解构建了一个可以实现“动态创建”和“运行时类型识别”以及“永久保存”的“类层级结构”。再贴代码之前,让我再说说这四个名词的意思吧,实际上,我认为,他们只是一个问题的四个方面

因为我们要实现最纯粹的面对对象编程,即按照“万物皆对象”的观点,能操作的所有东西都应该是某个类的对象,而类与类之间的关系,应该组织在一张大“网”中,什么类的子类是什么,万物的源头是什么类,等等。库类框架中各类的关系,构成了类层级结构

既然万物皆对象,我们有理由要求像保存数据一样将我们操作的对象保存在磁盘上,这就是永久保存技术,而且在需要的时候从磁盘读入内存并再现这个对象,这就是动态创建技术;这两个技术要求我们必须做到,在一个类被创建出来后,还能获知他是什么类的对象,这就是运行时类型识别技术;

让我们来想想运行时类型识别
例如,在Human类->Man类->Child类这样的框架中,要执行一个全局的FindAWomanToMarry(Human *p),就要先确定当前的p是什么?当然,我们知道它是Human类,子类和父类总是有Is-A关系,即Chinld类的对象是一个 Human类对象( Child IS A Human)。但是我们无法知道p表示的是Human的那个子类的对象,当然我们有其它办法例如获取年龄值等等来避开这个特定的问题,但是并没有回答我们为了说明的那个问题:怎么在一个类的对象被创建出来之后还能得到这个对象在类层级结构中的位置

说了这么多,总而言之,我认为,运行时类型识别、动态创建、永久保存和类层级结构是面对对象语言(或框架)所必须提供的特性。MFC框架和《深入浅出MFC》告诉我们,如何在C++中构造出这出这些特性。

接下来附上多年前的依样画葫芦的一些代码,共四个文件。

RTCT.h :包含非常精妙的宏定义,是实现运行时类型识别的关键

#ifndef RTTCTRT
#define RTTCTRT
#define FIRST_CLASS Human
#define RTTI_PRE(X)  \
     static RUNTIMECLASS X##class;\
     virtual RUNTIMECLASS* GetRunTimeClass() const;
#define RTTI_LINK(new_class,basic_class)\
static char new_class##_p[]=#new_class;\
RUNTIMECLASS new_class::new_class##class={\
        new_class##_p,sizeof(new_class),&basic_class::basic_class##class,NULL,NULL};\
static RFX_init init##new_class(&new_class::new_class##class);\
RUNTIMECLASS* new_class::GetRunTimeClass() const\
{return &new_class::new_class##class;}

#define RTCT_PRE(X) \
    static RUNTIMECLASS X##class;\
    virtual RUNTIMECLASS* GetRunTimeClass() const;\
    static Human* RUNTIMECREAT();
#define RTCT_LINK(new_class,basic_class)\
Human *new_class::RUNTIMECREAT()\
{return new new_class; }\
static char new_class##_p[]=#new_class;\
RUNTIMECLASS new_class::new_class##class={\
new_class##_p,sizeof(new_class),&basic_class::basic_class##class,new_class::RUNTIMECREAT,NULL};\
static RFX_init init##new_class(&new_class::new_class##class);\
RUNTIMECLASS* new_class::GetRunTimeClass() const\
{return &new_class::new_class##class;}
/////////////////////////////////////////////////
    class FIRST_CLASS;
/*在引用之前,这是非常必要的*/
struct RUNTIMECLASS
{
char * name; 
int size;
RUNTIMECLASS *basic;//指向父类
    FIRST_CLASS* (*pf_Creat)();
    FIRST_CLASS* RTCreat();
RUNTIMECLASS *next;//指向下一节点
static  RUNTIMECLASS *First;
};
/////////////////////////////////////////////////
class RFX_init
{
public:
RFX_init(RUNTIMECLASS *newclass);
};
#endif

UDF.h:自己构建的几个类,形成一个以Human类为源头的小框架

#ifndef UDFF
#define UDFF
#define R_DEBUG
#include "RTCT.h"
int CMP(char s1[],char s2[]);
class Human
{
public:
Human(int a=1,int i=1);
inline int Getage(){return age;};
inline int GetID(){return age;};
RTTI_PRE(Human)
private:
int age;
int ID;
};
////////////////////////////
class Man:public Human
{
public:
Man(int a=1,int i=1,int ifm=1);
RTCT_PRE(Man)
inline int GetMarryStatue(){return ifmarry;};
private:
int ifmarry;
};
///////////////////////////
class Woman:public Human
{
public:
Woman(int a=1,int i=1,int ifm=1);
RTCT_PRE(Woman)
inline int GetMarryStatue(){return ifmarry;};
private:
int ifmarry;
};
//////////////////////////////////
class Child:public Human
{
public:
Child(int a=1,int i=1,int g=1);
RTTI_PRE(Child)
inline int GetGrade(){return grade;};
private:
int grade;
};
//////////////////////////////////
class Baby:public Child
{
public:
Baby(int a=1,int i=1,int g=1,int iq=1);
RTTI_PRE(Baby)
inline int GetIQ(){return IQ;};
private:
int IQ;
};
#endif

UDF.cpp:自己构建的几个类,具体实现


#include "UDF.h"
#include <iostream.h>
#include <string.h>
RFX_init::RFX_init(RUNTIMECLASS *newclass)
{
#ifdef R_DEBUG
if(RUNTIMECLASS::First!=NULL)
{cout<<"First_pre"<<"["<<RUNTIMECLASS::First->name<<"]"<<endl;}
else{cout<<"First_pre"<<"NULL"<<endl;} #endif ///////////////初始化链表/////////// newclass->next =RUNTIMECLASS::First;
    RUNTIMECLASS::First=newclass;
/////////////////////////
    #ifdef R_DEBUG
cout<<"["<<newclass->name<<"]"<<"LINKED"<<endl;
cout<<"First_"<<"["<<RUNTIMECLASS::First->name<<"]"<<endl;
cout<<"*****************************"<<endl<<endl;
    #endif
}
RUNTIMECLASS* RUNTIMECLASS::First =NULL;
static char Human_p[]="Human";
RUNTIMECLASS Human::Humanclass={Human_p,sizeof(Human),NULL,NULL,NULL};
RFX_init initHuman(&amp;Human::Humanclass);
RUNTIMECLASS* Human::GetRunTimeClass() const
{return &amp;Human::Humanclass;}
Human::Human(int a,int i)
{
cout<<"Human Creat."<<endl;
age=a;
ID=i;
}
Man::Man(int a,int i,int ifm):Human(a,i)
{
cout<<"Man Creat."<<endl;
ifmarry=ifm;
}
Woman::Woman(int a,int i,int ifm):Human(a,i)
{
cout<<"Woman Creat."<<endl;
ifmarry=ifm;
}
Child::Child(int a,int i,int g):Human(a,i)
{
cout<<"Child Creat."<<endl;
grade=g;
}
Baby::Baby(int a,int i,int g,int iq):Child(a,i,g)
{
cout<<"Baby Creat."<<endl;
IQ=iq;
}
Human* RUNTIMECLASS::RTCreat()
{
if(pf_Creat==NULL)
{
  cout<<"此子类不支持动态创建。"<<endl;
  return NULL;
}
else 
{
  return (*pf_Creat)();
}
}
int CMP(char s1[],char s2[])
{
int i=0;
if (strlen(s1)!=strlen(s2))
     return 0;
for(;s1[i]!='\0';i++)
{
  if(s1[i]!=s2[i])
   return 0;
}
return 1;
}
RTTI_LINK(Baby,Child)
RTCT_LINK(Man,Human)
RTCT_LINK(Woman,Human)
RTTI_LINK(Child,Human)

main.cpp:通过往这个框架中添加一个类来说明如何使用这些特性


#include <iostream.h>
#include <stdlib.h>
#include "UDF.h"

class Rainyboy:public Baby
{
public:
Rainyboy(int a=1,int i=1,int g=1,int iq=1);
~Rainyboy();
RTCT_PRE(Rainyboy)
};
Rainyboy::Rainyboy(int a,int i,int g,int iq):Baby(a,i,g,iq)
{
cout<<"Rainyboy Creat."<<endl;
}
Rainyboy::~Rainyboy(){cout<<"Rainyboy Broken";};
    RTCT_LINK(Rainyboy,Baby)
int main()
{
RUNTIMECLASS *pnow;
RUNTIMECLASS *pbas;
Human *newp; 
char s[10];
cout<<"**********"<<endl<<"类别目录表"<<endl<<"**********"<<endl; for(pnow=RUNTIMECLASS::First;pnow!=NULL;pnow=pnow->next)
{
  cout<<pnow->name<<"["<<pnow->size<<"]"; for(pbas=pnow->basic;pbas!=NULL;pbas=pbas->basic)
   cout<<"<-"<<pbas->name<<"["<<pbas->size<<"]";
        cout<<endl;
}
cout<<"***********************************"<<endl;
cout<<"动态创建测试,输入要创建的类名:"; cin>>s; 
cout<<"***********************************"<<endl; for(pnow=RUNTIMECLASS::First;pnow!=NULL;pnow=pnow->next)
{
  if(CMP(pnow->name,s))
  {
   cout<<"Class "<<pnow->name<<"  FOUNED!"<<endl; newp=pnow->RTCreat();
   break;
  }
}
if(pnow==NULL)
  cout<<"Class "<<s<<" NOT FOUNED!"<<endl;
delete newp;
return 1;
}

程序运行后,显示:
1

输入要创建的类名后,显示:
2

说明动态创建成功。

PS: 花了些时间写了这个有点长的东西,是因为随着学习Python的推进,觉得很多东西都是似曾相识,是已有技术的另一种实现。因此翻出这些老古董代码,整理自己的思路,就当是抛砖引玉吧,呵呵!

PS2: 《深入浅出MFC》是一本不可多得的好书,今日再读,更觉得它是求知路上不可多得的良师益友,全书四篇:“勿在浮沙筑高台”,“欲善工事先利其器”,“浅出MFC程序设计”,“深入MFC程序设计”,不急不躁,带领读者慢慢品味代码之美,编程之乐,令人流连忘返!

About 范雨

Talk is cheap. Show me the code.
此条目发表在C++, 设计模式, Python分类目录。将固定链接加入收藏夹。

留下评论