用C编写面向对象代码有哪些方法?特别是在多态性方面。
另请参阅此堆栈溢出问题C中的面向对象。
用C编写面向对象代码有哪些方法?特别是在多态性方面。
另请参阅此堆栈溢出问题C中的面向对象。
当前回答
我认为首先要说的是(至少在我看来)C的函数指针实现真的很难使用。我会跳过一大堆的圆环来避免函数指针…
也就是说,我认为其他人说的很好。你有结构,你有模块,而不是foo->方法(a,b,c),你最终用方法(foo,a,b,c)如果你有一个“method”方法,那么你可以用类型前缀它,所以FOO_method(foo,a,b,c),正如其他人所说…通过良好地使用.h文件,您可以获得私有和公共文件,等等。
现在,有一些事情是这个技巧不能给你的。它不会提供私有数据字段。我认为,这与意志力和良好的编码习惯有关……而且,没有一种简单的方法来继承它。
这些至少是简单的部分……其余的,我认为是90/10的情况。10%的收益需要90%的工作……
其他回答
既然你说的是多态性,那么是的,你可以,我们在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++最初是作为一个生成C代码的中间步骤的预处理器开始的。
本质上,您最终要做的是为所有方法创建一个调度表,其中存储函数引用。派生类需要复制这个分派表并替换您想要重写的条目,如果您的新“方法”想要调用基方法,则必须调用原始方法。最终,你要重写c++。
添加一点OOC代码:
#include <stdio.h>
struct Node {
int somevar;
};
void print() {
printf("Hello from an object-oriented C method!");
};
struct Tree {
struct Node * NIL;
void (*FPprint)(void);
struct Node *root;
struct Node NIL_t;
} TreeA = {&TreeA.NIL_t,print};
int main()
{
struct Tree TreeB;
TreeB = TreeA;
TreeB.FPprint();
return 0;
}
我建议使用Objective-C,它是C的超集。
虽然Objective-C已经有30年的历史了,但它允许编写优雅的代码。
http://en.wikipedia.org/wiki/Objective-C
当然这是可能的。这就是所有GTK+和GNOME所基于的框架GObject所做的工作。