用C编写面向对象代码有哪些方法?特别是在多态性方面。
另请参阅此堆栈溢出问题C中的面向对象。
用C编写面向对象代码有哪些方法?特别是在多态性方面。
另请参阅此堆栈溢出问题C中的面向对象。
当前回答
如果您确信面向对象的方法对于您正在尝试解决的问题更优越,那么为什么还要尝试使用非面向对象的语言来解决它呢?看来你用错工具了。使用c++或其他面向对象的C语言变体。
如果你问这个问题是因为你要开始在一个已经存在的用C编写的大型项目上编写代码,那么你不应该试图将你自己的(或其他人的)OOP范式强加到项目的基础设施中。遵循项目中已经存在的指导方针。一般来说,干净的api和独立的库和模块将有助于实现干净的面向对象的设计。
如果,在所有这些之后,你真的打算做OOP C,请阅读这篇文章(PDF)。
其他回答
既然你说的是多态性,那么是的,你可以,我们在c++出现之前几年就已经在做这类事情了。
基本上,你使用一个结构体来保存数据和一个函数指针列表,以指向该数据的相关函数。
因此,在一个通信类中,你会有一个打开、读、写和关闭调用,它将被维护为结构中的四个函数指针,与对象的数据一起,类似于:
typedef struct {
int (*open)(void *self, char *fspec);
int (*close)(void *self);
int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
// And data goes here.
} tCommClass;
tCommClass commRs232;
commRs232.open = &rs232Open;
: :
commRs232.write = &rs232Write;
tCommClass commTcp;
commTcp.open = &tcpOpen;
: :
commTcp.write = &tcpWrite;
当然,上面的那些代码段实际上是在诸如rs232Init()这样的“构造函数”中。
当你“继承”这个类时,你只需要改变指针指向你自己的函数。每个调用这些函数的人都会通过函数指针来做,给你你的多态性:
int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");
有点像手动虚表。
你甚至可以通过将指针设置为NULL来创建虚拟类——这与c++的行为略有不同(运行时的核心转储而不是编译时的错误)。
下面是一段演示它的示例代码。首先是顶级类结构:
#include <stdio.h>
// The top-level class.
typedef struct sCommClass {
int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;
然后我们有TCP '子类'的函数:
// Function for the TCP 'class'.
static int tcpOpen (tCommClass *tcp, char *fspec) {
printf ("Opening TCP: %s\n", fspec);
return 0;
}
static int tcpInit (tCommClass *tcp) {
tcp->open = &tcpOpen;
return 0;
}
HTTP也是一样:
// Function for the HTTP 'class'.
static int httpOpen (tCommClass *http, char *fspec) {
printf ("Opening HTTP: %s\n", fspec);
return 0;
}
static int httpInit (tCommClass *http) {
http->open = &httpOpen;
return 0;
}
最后是一个测试程序来展示它的作用:
// Test program.
int main (void) {
int status;
tCommClass commTcp, commHttp;
// Same 'base' class but initialised to different sub-classes.
tcpInit (&commTcp);
httpInit (&commHttp);
// Called in exactly the same manner.
status = (commTcp.open)(&commTcp, "bigiron.box.com:5000");
status = (commHttp.open)(&commHttp, "http://www.microsoft.com");
return 0;
}
这将产生输出:
Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com
你可以看到不同的函数被调用,取决于子类。
面向对象的C,可以做到,我在韩国看过这种类型的代码,这是我多年来见过的最可怕的怪物(这是去年(2007年)我看到的代码)。 所以,是的,这是可以做到的,是的,人们曾经这样做过,甚至在这个时代仍然这样做。但我还是推荐c++或Objective-C,这两种语言都起源于C,目的是用不同的范式提供面向对象。
我建立了一个小图书馆,在那里我尝试了,对我来说,它真的很有效。所以我想和你分享这段经历。
https://github.com/thomasfuhringer/oxygen
使用结构并将其扩展到每个其他子类,可以很容易地实现单继承。对父结构进行简单的强制转换,就可以在所有后代上使用父方法。 只要知道变量指向持有此类对象的结构体,就可以始终将其转换为根类并进行自省。
正如前面提到的,虚拟方法有点棘手。但它们是可行的。为了保持简单,我只是在类描述结构中使用了一个函数数组,每个子类在需要的地方复制和重新填充单独的插槽。
多重继承实现起来相当复杂,并且会对性能产生重大影响。所以我离开了。我确实认为,在相当多的情况下,清晰地模拟现实生活环境是可取和有用的,但在可能90%的情况下,单一继承可以满足需求。而且单一继承很简单,没有成本。
我也不关心类型安全。我认为你不应该依赖编译器来防止你的编程错误。而且无论如何,它只能保护您避免相当小部分的错误。
通常,在面向对象的环境中,您还希望实现引用计数以尽可能地自动化内存管理。因此,我还在“Object”根类中放入了引用计数,并使用了一些功能来封装堆内存的分配和释放。
它非常简单和精简,给了我OO的基本要领,而不强迫我去处理c++这个怪物。而且我保留了留在C领域的灵活性,这使得集成第三方库更加容易。
如果您确信面向对象的方法对于您正在尝试解决的问题更优越,那么为什么还要尝试使用非面向对象的语言来解决它呢?看来你用错工具了。使用c++或其他面向对象的C语言变体。
如果你问这个问题是因为你要开始在一个已经存在的用C编写的大型项目上编写代码,那么你不应该试图将你自己的(或其他人的)OOP范式强加到项目的基础设施中。遵循项目中已经存在的指导方针。一般来说,干净的api和独立的库和模块将有助于实现干净的面向对象的设计。
如果,在所有这些之后,你真的打算做OOP C,请阅读这篇文章(PDF)。
我已经研究这个一年了
由于GObject系统很难用纯C语言使用,所以我尝试编写一些不错的宏来简化C语言的OO风格。
#include "OOStd.h"
CLASS(Animal) {
char *name;
STATIC(Animal);
vFn talk;
};
static int Animal_load(Animal *THIS,void *name) {
THIS->name = name;
return 0;
}
ASM(Animal, Animal_load, NULL, NULL, NULL)
CLASS_EX(Cat,Animal) {
STATIC_EX(Cat, Animal);
};
static void Meow(Animal *THIS){
printf("Meow!My name is %s!\n", THIS->name);
}
static int Cat_loadSt(StAnimal *THIS, void *PARAM){
THIS->talk = (void *)Meow;
return 0;
}
ASM_EX(Cat,Animal, NULL, NULL, Cat_loadSt, NULL)
CLASS_EX(Dog,Animal){
STATIC_EX(Dog, Animal);
};
static void Woof(Animal *THIS){
printf("Woof!My name is %s!\n", THIS->name);
}
static int Dog_loadSt(StAnimal *THIS, void *PARAM) {
THIS->talk = (void *)Woof;
return 0;
}
ASM_EX(Dog, Animal, NULL, NULL, Dog_loadSt, NULL)
int main(){
Animal *animals[4000];
StAnimal *f;
int i = 0;
for (i=0; i<4000; i++)
{
if(i%2==0)
animals[i] = NEW(Dog,"Jack");
else
animals[i] = NEW(Cat,"Lily");
};
f = ST(animals[0]);
for(i=0; i<4000; ++i) {
f->talk(animals[i]);
}
for (i=0; i<4000; ++i) {
DELETE0(animals[i]);
}
return 0;
}
这是我的项目网站(我没有足够的时间写en。Doc,不过中文的Doc要好得多)。
东方海外海湾合作委员会