用C编写面向对象代码有哪些方法?特别是在多态性方面。


另请参阅此堆栈溢出问题C中的面向对象。


当前回答

是的,你可以。在c++或Objective-C出现之前,人们就开始编写面向对象的C语言了。在某种程度上,c++和Objective-C都试图采用C中使用的一些面向对象概念,并将它们形式化为语言的一部分。

这是一个非常简单的程序,它展示了如何创建一个看起来像方法调用的东西(有更好的方法可以做到这一点。这只是证明语言支持这些概念):

#include<stdio.h>

struct foobarbaz{
    int one;
    int two;
    int three;
    int (*exampleMethod)(int, int);
};

int addTwoNumbers(int a, int b){
    return a+b;
}

int main()
{
    // Define the function pointer
    int (*pointerToFunction)(int, int) = addTwoNumbers;

    // Let's make sure we can call the pointer
    int test = (*pointerToFunction)(12,12);
    printf ("test: %u \n",  test);

    // Now, define an instance of our struct
    // and add some default values.
    struct foobarbaz fbb;
    fbb.one   = 1;
    fbb.two   = 2;
    fbb.three = 3;

    // Now add a "method"
    fbb.exampleMethod = addTwoNumbers;

    // Try calling the method
    int test2 = fbb.exampleMethod(13,36);
    printf ("test2: %u \n",  test2);

    printf("\nDone\n");
    return 0;
}

其他回答

既然你说的是多态性,那么是的,你可以,我们在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语言,没有宏预处理。它具有继承、多态、数据封装(包括私有数据)。它没有等效的受保护限定符,这意味着私有数据在整个继承链中也是私有的。

#include "triangle.h"
#include "rectangle.h"
#include "polygon.h"

#include <stdio.h>

int main()
{
    Triangle tr1= CTriangle->new();
    Rectangle rc1= CRectangle->new();

    tr1->width= rc1->width= 3.2;
    tr1->height= rc1->height= 4.1;

    CPolygon->printArea((Polygon)tr1);

    printf("\n");

    CPolygon->printArea((Polygon)rc1);
}

/*output:
6.56
13.12
*/

是的,但我从未见过有人尝试用C实现任何类型的多态性。

是的。事实上,Axel Schreiner免费提供了他的书《ANSI-C中的面向对象编程》,这本书相当全面地涵盖了这个主题。

您可能需要做的一件事是研究X Window的Xt工具包的实现。当然,它已经很老了,但是许多使用的结构都是在传统的c语言中以面向对象的方式设计的。一般来说,这意味着在这里或那里添加一个额外的间接层,并设计相互覆盖的结构。

你真的可以在C语言中以面向对象的方式做很多事情,即使有时感觉,面向对象的概念并没有完全从#include<favorite_OO_Guru.h>的思想中涌现出来。它们确实构成了当时许多公认的最佳实践。面向对象语言和系统只是提炼和放大了当时编程时代精神的一部分。