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


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


当前回答

是的,这是可能的。

这是纯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
*/

其他回答

我建立了一个小图书馆,在那里我尝试了,对我来说,它真的很有效。所以我想和你分享这段经历。

https://github.com/thomasfuhringer/oxygen

使用结构并将其扩展到每个其他子类,可以很容易地实现单继承。对父结构进行简单的强制转换,就可以在所有后代上使用父方法。 只要知道变量指向持有此类对象的结构体,就可以始终将其转换为根类并进行自省。

正如前面提到的,虚拟方法有点棘手。但它们是可行的。为了保持简单,我只是在类描述结构中使用了一个函数数组,每个子类在需要的地方复制和重新填充单独的插槽。

多重继承实现起来相当复杂,并且会对性能产生重大影响。所以我离开了。我确实认为,在相当多的情况下,清晰地模拟现实生活环境是可取和有用的,但在可能90%的情况下,单一继承可以满足需求。而且单一继承很简单,没有成本。

我也不关心类型安全。我认为你不应该依赖编译器来防止你的编程错误。而且无论如何,它只能保护您避免相当小部分的错误。

通常,在面向对象的环境中,您还希望实现引用计数以尽可能地自动化内存管理。因此,我还在“Object”根类中放入了引用计数,并使用了一些功能来封装堆内存的分配和释放。

它非常简单和精简,给了我OO的基本要领,而不强迫我去处理c++这个怪物。而且我保留了留在C领域的灵活性,这使得集成第三方库更加容易。

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

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

动物和狗的小例子:你镜像了c++的虚表机制(基本上是这样)。你还分离了分配和实例化(Animal_Alloc, Animal_New),所以我们不会多次调用malloc()。我们还必须显式地传递this指针。

如果你要做非虚函数,那就很简单了。你只是不需要将它们添加到虚函数表中,静态函数也不需要this指针。多重继承通常需要多个虚表来解决歧义。

此外,您应该能够使用setjmp/longjmp来进行异常处理。

struct Animal_Vtable{
    typedef void (*Walk_Fun)(struct Animal *a_This);
    typedef struct Animal * (*Dtor_Fun)(struct Animal *a_This);

    Walk_Fun Walk;
    Dtor_Fun Dtor;
};

struct Animal{
    Animal_Vtable vtable;

    char *Name;
};

struct Dog{
    Animal_Vtable vtable;

    char *Name; // Mirror member variables for easy access
    char *Type;
};

void Animal_Walk(struct Animal *a_This){
    printf("Animal (%s) walking\n", a_This->Name);
}

struct Animal* Animal_Dtor(struct Animal *a_This){
    printf("animal::dtor\n");
    return a_This;
}

Animal *Animal_Alloc(){
    return (Animal*)malloc(sizeof(Animal));
}

Animal *Animal_New(Animal *a_Animal){
    a_Animal->vtable.Walk = Animal_Walk;
    a_Animal->vtable.Dtor = Animal_Dtor;
    a_Animal->Name = "Anonymous";
    return a_Animal;
}

void Animal_Free(Animal *a_This){
    a_This->vtable.Dtor(a_This);

    free(a_This);
}

void Dog_Walk(struct Dog *a_This){
    printf("Dog walking %s (%s)\n", a_This->Type, a_This->Name);
}

Dog* Dog_Dtor(struct Dog *a_This){
    // Explicit call to parent destructor
    Animal_Dtor((Animal*)a_This);

    printf("dog::dtor\n");

    return a_This;
}

Dog *Dog_Alloc(){
    return (Dog*)malloc(sizeof(Dog));
}

Dog *Dog_New(Dog *a_Dog){
    // Explict call to parent constructor
    Animal_New((Animal*)a_Dog);

    a_Dog->Type = "Dog type";
    a_Dog->vtable.Walk = (Animal_Vtable::Walk_Fun) Dog_Walk;
    a_Dog->vtable.Dtor = (Animal_Vtable::Dtor_Fun) Dog_Dtor;

    return a_Dog;
}

int main(int argc, char **argv){
    /*
      Base class:

        Animal *a_Animal = Animal_New(Animal_Alloc());
    */
    Animal *a_Animal = (Animal*)Dog_New(Dog_Alloc());

    a_Animal->vtable.Walk(a_Animal);

    Animal_Free(a_Animal);
}

PS.这是在c++编译器上测试的,但是在C编译器上运行应该很容易。

OOP只是一个范例,在程序中数据比代码更重要。OOP不是一种语言。因此,就像普通C是一种简单的语言一样,普通C中的OOP也很简单。

是的,这是可能的。

这是纯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
*/