纯粹的面向对象的 C 语言






3.50/5 (6投票s)
本文档描述了一种在纯 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
),两个名为 Talk
和 IsDomesticated
的虚方法,以及两个非虚方法,名为 Report
和 SaySpecies
。
以下是 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++ 不同,虚方法
Talk
和IsDomesticated
不指定参数列表。在 SOOC 中,参数列表在实现方法时指定,并且必须与调用方法时指定的参数兼容。 - 与 C++ 不同,
this
参数明确指定为非虚方法中的第一个参数。请注意,特殊宏CLASS
(在类顶部定义)用于以更通用的方式引用类名。
即使没有指定虚方法或非虚方法,也需要宏对 BEGIN_VIRTUAL_METHODS
、END_VIRTUAL_METHODS
、BEGIN_NONVIRTUAL_METHODS
和 END_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
的类,它将派生自 Animal
。Who
类的定义顶部如下:
/************************************************************/
#undef CLASS
#define CLASS Who
#undef BASECLASS
#define BASECLASS Animal
/***********************************************************/
实现文件中的 Overrides
部分如下:
BEGIN_OVERRIDES
OVERRIDE( Animal, IsDomesticated )
END_OVERRIDES
请注意,这表明 Animal
类中的方法正在被覆盖。
类实例化
与 C++ 一样,类可以在栈上或自由存储区(堆)上实例化。与 C++ 不同,类构造函数和析构函数必须手动调用。这是通过特殊的 CONSTRUCT
和 DESTRUCT
宏完成的。以下是栈上实例化 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
类。
方法访问
方法使用 CALL
和 VCALL
宏调用,具体取决于方法是非虚还是虚。以下是在 pCat
实例上调用 GetName
方法的示例,其中 GetName
是在基类 Who
中定义的方法:
CALL( Who, GetName )( (Who*)pCat, name );
VCALL
宏需要更多参数。在此示例中,VCALL
宏用于调用 pCat
实例的 Talk
方法。回想一下 Talk
方法是在 Animal
类中定义的,并且返回 void
。在进行方法调用时必须提供此信息,因为虚方法的原型未在方法定义中指定。调用如下:
VCALL( RVOID, Animal, Talk, pCat )( pCat );
VCALL
的参数如下:
- 方法的返回类型。
RVOID
、RLONG
等。 - 定义虚方法的类。
- 要调用的方法。
- 正在操作的实例。
第二组括号,在此例中为 (pCat
),表示函数的参数。在此例中,只传递了 this
指针。
使用设置
在使用类之前,一个源文件(通常是定义 main 函数的那个)将在特殊的声明宏 BEGIN_DECLARE_APPLICATION_CLASSES
和 END_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 库的描述。有关类如何实例化、使用、定义和实现的更多信息,请参阅随附的示例代码。