65.9K
CodeProject 正在变化。 阅读更多。
Home

纯粹的面向对象的 C 语言

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.50/5 (6投票s)

2007年12月15日

CPOL

5分钟阅读

viewsIcon

43867

downloadIcon

283

本文档描述了一种在纯 C(包括标准/ANSI C 和一些预 ANSI C 的变体)中使用面向对象编程 (OOP) 特性的方法。

概述

本文档描述了一种在纯 C(包括标准/ANSI C 和一些预 ANSI C 的变体)中使用面向对象编程 (OOP) 特性的方法。已测试以下编译器:

  • Visual Studio 6.x +
  • GCC
  • LCC
  • Turbo C 2.0

OOP 结构通过 C 宏实现,因此源代码易于阅读。假定读者了解 OOP 以及 C++ 的基础知识。

背景

这可以作为一项学术练习,供希望了解如何在非面向对象语言中手动构建面向对象特性的读者参考。在需要使用 C 编译器作为特定领域语言的中间步骤,或者目标设备编译器支持有限的情况下,它也可能具有实际用途。

定义类

要使用此框架,每个文件都应包含 SOOC.h 文件。

SOOC 系统中的每个类都派生自另一个类。如果一个类不需要基类,则使用一个名为 Object 的特殊类作为基类。每个类文件的定义都在单独的头文件中。

因为通过示例学习是最好的方式,这里展示了一个示例。

在此示例中,Animal 类派生自 Object。该类包含一个数据成员(一个 31 字符的字符串,名为 _species),两个名为 TalkIsDomesticated 的虚方法,以及两个非虚方法,名为 ReportSaySpecies

以下是 C++ 和 SOOC 中类定义的并排比较:

C++ SOOC
//**********************
//  CLASS Animal
//**********************

class Animal
{

public:
  // Data members
  char m_species[32];  

  // Virtual methods
  virtual void Talk();
  virtual int 
    IsDomesticated();

  // Non-virtual methods
  void Report();
  void SaySpecies();
};






/*********************************/
#undef  CLASS
#define CLASS        Animal
#undef  BASECLASS
#define BASECLASS    Object
/********************************/

BEGIN_CLASS
 
  /* Data members */
  char m_species[32];
      
  /* Virtual methods */
  BEGIN_VIRTUAL_METHODS
    VIRTUAL_METHOD( Talk )
    VIRTUAL_METHOD( IsDomesticated )
  END_VIRTUAL_METHODS
      
  /* Non-virtual methods */
  BEGIN_NONVIRTUAL_METHODS
    NONVIRTUAL_METHOD( void, Report )( CLASS* this );
    NONVIRTUAL_METHOD( void, SaySpecies )( CLASS* this );
  END_NONVIRTUAL_METHODS
      
END_CLASS

与 C++ 的区别

  • 与 C++ 不同,虚方法 TalkIsDomesticated 不指定参数列表。在 SOOC 中,参数列表在实现方法时指定,并且必须与调用方法时指定的参数兼容。
  • 与 C++ 不同,this 参数明确指定为非虚方法中的第一个参数。请注意,特殊宏 CLASS(在类顶部定义)用于以更通用的方式引用类名。

即使没有指定虚方法或非虚方法,也需要宏对 BEGIN_VIRTUAL_METHODSEND_VIRTUAL_METHODSBEGIN_NONVIRTUAL_METHODSEND_NONVIRTUAL_METHODS

实现类

每个类文件的实现定义在单独的源文件中。继续上面的示例,通过实现该类:

C++ SOOC
Animal::Animal()
{
  strcpy( this->m_species, 
          "Animal" );
}
 
Animal::~Animal()
{}
 
void Animal::SaySpecies()
{
  ...some implementation...
}
 
void Animal::Report()
{
  ...some implementation...
}
 
void Animal::Talk()
{
  ...some implementation...
}
 
int Animal::IsDomesticated()
{
  ...some implementation...
}

 
 
 
 
 
  
 
 
 
 

 


BEGIN_CLASS_IMPLEMENTATION

  BEGIN_CONSTRUCTOR
  {
    strcpy( this->m_species, "Animal" );
  }
  END_CONSTRUCTOR
 
  BEGIN_DESTRUCTOR
  {}
  END_DESTRUCTOR
 
      
  BEGIN_NONVIRTUAL_METHOD( void, SaySpecies )( CLASS* this )
  {
    ...some implementation...
  }
  END_NONVIRTUAL_METHOD
      
  BEGIN_NONVIRTUAL_METHOD( void, Report )( CLASS* this )
  {
    ...some implementation...
  }
  END_NONVIRTUAL_METHOD
 
  BEGIN_VIRTUAL_METHOD( void, Talk )( CLASS* this )
  {
    ...some implementation...
  }
  END_VIRTUAL_METHOD
 
  BEGIN_VIRTUAL_METHOD( int, IsDomesticated )( CLASS* this )
  {
    ...some implementation...
  }
  END_VIRTUAL_METHOD
      
  BEGIN_OVERRIDES
    OVERRIDE( Animal, Talk )
    OVERRIDE( Animal, IsDomesticated )
  END_OVERRIDES

END_CLASS_IMPLEMENTATION

与 C++ 的区别

  • 每个类都必须实现一个显式的构造函数和一个析构函数,即使它们是空的。
  • 每个方法的实现都必须指明它是实现虚方法还是非虚方法。

请注意类末尾的 BEGIN_OVERRIDES / END_OVERRIDES 宏。它们列出了此类覆盖的虚方法。宏 OVERRIDE 接受两个参数:定义该方法的类名和被覆盖的方法名。

继承类

这里有一个名为 Who 的类,它将派生自 AnimalWho 类的定义顶部如下:

/************************************************************/
#undef CLASS
#define CLASS Who
#undef BASECLASS
#define BASECLASS Animal
/***********************************************************/

实现文件中的 Overrides 部分如下:

BEGIN_OVERRIDES 
    OVERRIDE( Animal, IsDomesticated )
END_OVERRIDES

请注意,这表明 Animal 类中的方法正在被覆盖。

类实例化

与 C++ 一样,类可以在栈上或自由存储区(堆)上实例化。与 C++ 不同,类构造函数和析构函数必须手动调用。这是通过特殊的 CONSTRUCTDESTRUCT 宏完成的。以下是栈上实例化 Animal 类的示例:

Animal animal; 
CONSTRUCT( Animal, &animal );
...use the instance... 
DESTRUCT( &animal );

以下是自由存储区上实例化类的示例。这是使用特殊的 NEW 宏完成的。

Cat* pCat = NEW( Cat );
CONSTRUCT( Cat, pCat );
...use the instance...
DELETE( pCat );

请注意,在动态实例化类的情况下,使用的是 DELETE 宏,而不是 DESTRUCT 宏。DELETE 宏会在内部析构实例,然后删除与之关联的内存。

数据成员访问

Cat 类派生自 Who。其构造函数设置 Animal 类的数据成员 m_species。但是,与 C++ 不同,数据成员的命名空间不会自动继承。也就是说,如果这是 C++,从 Cat 方法的体内,您可以像数据成员 m_species 定义在 Cat 类中一样访问它。然而,在 SOOC 中,必须专门引用定义数据成员的类名。例如:

BEGIN_CONSTRUCTOR 
{ 
    strcpy( ((Animal*)this)->m_species, "Cat" );
} 
END_CONSTRUCTOR

因为 m_species 定义在 Animal 类中,所以由 this 变量指定的 Cat 类实例首先被强制转换为 Animal 类。

方法访问

方法使用 CALLVCALL 宏调用,具体取决于方法是非虚还是虚。以下是在 pCat 实例上调用 GetName 方法的示例,其中 GetName 是在基类 Who 中定义的方法:

CALL( Who, GetName )( (Who*)pCat, name );

VCALL 宏需要更多参数。在此示例中,VCALL 宏用于调用 pCat 实例的 Talk 方法。回想一下 Talk 方法是在 Animal 类中定义的,并且返回 void。在进行方法调用时必须提供此信息,因为虚方法的原型未在方法定义中指定。调用如下:

VCALL( RVOID, Animal, Talk, pCat )( pCat );

VCALL 的参数如下:

  1. 方法的返回类型。RVOIDRLONG 等。
  2. 定义虚方法的类。
  3. 要调用的方法。
  4. 正在操作的实例。

第二组括号,在此例中为 (pCat),表示函数的参数。在此例中,只传递了 this 指针。

使用设置

在使用类之前,一个源文件(通常是定义 main 函数的那个)将在特殊的声明宏 BEGIN_DECLARE_APPLICATION_CLASSESEND_DECLARE_APPLICATION_CLASSES 中包含类列表。例如:

BEGIN_DECLARE_APPLICATION_CLASSES
    DECLARE_APPLICATION_CLASS( Animal )
    DECLARE_APPLICATION_CLASS( Who )
    DECLARE_APPLICATION_CLASS( Human )
    DECLARE_APPLICATION_CLASS( Dog )
    DECLARE_APPLICATION_CLASS( ShihTzu )
    DECLARE_APPLICATION_CLASS( Beagle )
    DECLARE_APPLICATION_CLASS( Cat )
    DECLARE_APPLICATION_CLASS( Giraffe )
    DECLARE_APPLICATION_CLASS( Cow )
END_DECLARE_APPLICATION_CLASSES

类必须按派生顺序列出。

结论

以上是对 SOOC 库的描述。有关类如何实例化、使用、定义和实现的更多信息,请参阅随附的示例代码。

© . All rights reserved.