524

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


另请参阅此 Stack Overflow 问题Object-orientation in C

4

32 回答 32

378

是的。事实上,Axel Schreiner 免费提供了他的书“ANSI-C 中的面向对象编程”,其中涵盖了相当全面的主题。

于 2008-12-09T04:12:20.740 回答
371

既然您在谈论多态性,那么是的,您可以,我们在 C++ 出现之前几年就在做这种事情。

基本上,您使用 astruct来保存数据和函数指针列表,以指向该数据的相关函数。

因此,在通信类中,您将有一个 open、read、write 和 close 调用,该调用将作为结构中的四个函数指针以及对象的数据进行维护,例如:

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");

有点像手动vtable。

您甚至可以通过将指针设置为 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

所以你可以看到不同的函数被调用,这取决于子类。

于 2008-12-09T04:06:09.987 回答
94

命名空间通常通过以下方式完成:

stack_push(thing *)

代替

stack::push(thing *)

要将C结构变成类似于C++类的东西,您可以:

class stack {
     public:
        stack();
        void push(thing *);
        thing * pop();
        static int this_is_here_as_an_example_only;
     private:
        ...
};

进入

struct stack {
     struct stack_type * my_type;
     // Put the stuff that you put after private: here
};
struct stack_type {
     void (* construct)(struct stack * this); // This takes uninitialized memory
     struct stack * (* operator_new)(); // This allocates a new struct, passes it to construct, and then returns it
     void (*push)(struct stack * this, thing * t); // Pushing t onto this stack
     thing * (*pop)(struct stack * this); // Pops the top thing off the stack and returns it
     int this_is_here_as_an_example_only;
}Stack = {
    .construct = stack_construct,
    .operator_new = stack_operator_new,
    .push = stack_push,
    .pop = stack_pop
};
// All of these functions are assumed to be defined somewhere else

并做:

struct stack * st = Stack.operator_new(); // Make a new stack
if (!st) {
   // Do something about it
} else {
   // You can use the stack
   stack_push(st, thing0); // This is a non-virtual call
   Stack.push(st, thing1); // This is like casting *st to a Stack (which it already is) and doing the push
   st->my_type.push(st, thing2); // This is a virtual call
}

我没有做析构函数或删除,但它遵循相同的模式。

this_is_here_as_an_example_only 就像一个静态类变量——在一个类型的所有实例之间共享。所有方法实际上都是静态的,除了有些采用 this *

于 2010-04-28T20:42:29.897 回答
59

我相信,除了本身有用之外,在 C 中实现 OOP 也是学习OOP 和了解其内部工作原理的绝佳方式。许多程序员的经验表明,要有效和自信地使用一项技术,程序员必须了解底层概念最终是如何实现的。在 C 中模拟类、继承和多态性正是在教导这一点。

为了回答最初的问题,这里有几个资源可以教如何在 C 中进行 OOP:

EmbeddedGurus.com 博客文章“C 中基于对象的编程”展示了如何在可移植 C 中实现类和单一继承:http: //embeddedgurus.com/state-space/2008/01/object-based-programming-in-c /

应用笔记“C+”——C 中的面向对象编程“展示了如何使用预处理器宏在 C 中实现类、单一继承和后期绑定(多态性):http: //www.state-machine.com/resources/cplus_3。 0_manual.pdf,示例代码可从http://www.state-machine.com/resources/cplus_3.0.zip获得

于 2010-04-29T00:25:37.677 回答
33

我已经看到它完成了。我不会推荐它。C++ 最初是以这种方式作为预处理器开始的,它生成 C 代码作为中间步骤。

本质上,您最终要做的是为存储函数引用的所有方法创建一个调度表。派生一个类将需要复制此调度表并替换您想要覆盖的条目,如果您的新“方法”想要调用基方法,则必须调用原始方法。最终,你最终会重写 C++。

于 2008-12-09T04:04:02.880 回答
27

当然这是可能的。这就是所有GTK+GNOME所基于的框架GObject所做的。

于 2008-12-09T04:16:45.303 回答
18

C stdio FILE 子库是一个很好的例子,说明了如何在纯 C 中创建抽象、封装和模块化。

继承和多态性——通常被认为对 OOP 至关重要的其他方面——并不一定能提供它们所承诺的生产力收益,并且已经提出了合理的 论点,即它们实际上会阻碍开发和对问题域的思考。

于 2010-04-29T03:09:35.157 回答
16

一个动物和狗的小例子:你反映了 C++ 的 vtable 机制(基本上无论如何)。您还将分配和实例化 (Animal_Alloc, Animal_New) 分开,因此我们不会多次调用 malloc()。我们还必须明确地传递this指针。

如果您要执行非虚拟功能,那很简单。您只是不将它们添加到 vtable 和静态函数不需要this指针。多重继承通常需要多个 vtable 来解决歧义。

此外,您应该能够使用 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 编译器上工作。

于 2008-12-09T10:45:21.587 回答
13

查看GObject。它意味着在 C 中是面向对象的,并且是您正在寻找的一种实现。但是,如果您真的想要 OO,请使用 C++ 或其他一些 OOP 语言。如果您习惯于处理 OO 语言,那么有时使用 GObject 可能会非常困难,但就像任何事情一样,您会习惯于约定和流程。

于 2010-04-28T19:53:40.463 回答
13

这读起来很有趣。我自己也一直在思考同样的问题,思考它的好处是:

  • 尝试想象如何在非 OOP 语言中实现 OOP 概念有助于我理解 OOp 语言(在我的例子中是 C++)的优势。这有助于我更好地判断是否将 C 或 C++ 用于给定类型的应用程序——其中一个的好处胜过另一个。

  • 在我浏览网页以获取有关这方面的信息和意见时,我发现一位正在为嵌入式处理器编写代码并且只有 C 编译器可用的作者: http ://www.eetimes.com/discussion/other/4024626/Object-Oriented -C-Creating-Foundation-Classes-Part-1

在他的案例中,用纯 C 语言分析和调整 OOP 概念是一个有效的追求。看来他愿意牺牲一些 OOP 概念,因为尝试在 C 中实现它们会导致性能开销受到影响。

我吸取的教训是,是的,它可以在一定程度上做到,是的,有一些很好的理由来尝试它。

最后,机器在旋转堆栈指针位,使程序计数器跳来跳去并计算内存访问操作。从效率的角度来看,您的程序执行的这些计算越少越好……但有时我们必须简单地支付这笔税,以便我们可以以一种最不容易受到人为错误影响的方式组织我们的程序。OOP 语言编译器努力优化这两个方面。程序员必须更加小心地用 C 等语言实现这些概念。

于 2011-06-16T20:56:28.297 回答
11

有几种技术可以使用。最重要的是更多如何拆分项目。我们在项目中使用在 .h 文件中声明的接口,并在 .c 文件中使用对象的实现。重要的部分是所有包含 .h 文件的模块只将对象视为 a void *,而 .c 文件是唯一知道结构内部的模块。

对于一个我们命名为 FOO 的类来说,像这样的例子:

在 .h 文件中

#ifndef FOO_H_
#define FOO_H_

...
 typedef struct FOO_type FOO_type;     /* That's all the rest of the program knows about FOO */

/* Declaration of accessors, functions */
FOO_type *FOO_new(void);
void FOO_free(FOO_type *this);
...
void FOO_dosomething(FOO_type *this, param ...):
char *FOO_getName(FOO_type *this, etc);
#endif

C 实现文件将是这样的。

#include <stdlib.h>
...
#include "FOO.h"

struct FOO_type {
    whatever...
};


FOO_type *FOO_new(void)
{
    FOO_type *this = calloc(1, sizeof (FOO_type));

    ...
    FOO_dosomething(this, );
    return this;
}

因此,我将指针显式地指向该模块的每个函数的对象。C++ 编译器隐式执行它,而在 C 中我们显式地写出来。

我真的this在我的程序中使用,以确保我的程序不会在 C++ 中编译,并且它具有在我的语法突出显示编辑器中以另一种颜色显示的优良属性。

FOO_struct 的字段可以在一个模块中修改,另一个模块甚至不需要重新编译即可使用。

有了这种风格,我已经掌握了 OOP(数据封装)的大部分优势。通过使用函数指针,甚至可以很容易地实现继承之类的东西,但老实说,它真的很少有用。

于 2010-04-28T20:26:05.913 回答
10

您可能会发现查看 Apple 的核心基础 API 文档很有帮助。它是一个纯 C API,但许多类型都桥接到了 Objective-C 对象等价物。

您可能还会发现查看 Objective-C 本身的设计很有帮助。它与 C++ 有点不同,因为对象系统是根据 C 函数定义的,例如objc_msg_send调用对象上的方法。编译器将方括号语法转换为这些函数调用,因此您不必知道它,但考虑到您的问题,您可能会发现了解它在幕后的工作原理很有用。

于 2010-04-28T19:58:36.117 回答
9

你可以使用函数指针来伪造它,事实上,我认为理论上可以将 C++ 程序编译成 C。

但是,将范式强加于语言而不是选择使用范式的语言几乎没有意义。

于 2008-12-09T04:05:56.243 回答
7

面向对象的 C,可以做到,我在韩国的生产环境中看到过这种类型的代码,这是我多年来见过的最可怕的怪物(就像去年(2007 年)我看到的代码一样)。所以是的,它可以做到,是的,人们以前做过,即使在这个时代仍然这样做。但我推荐 C++ 或 Objective-C,它们都是从 C 诞生的语言,目的是为面向对象提供不同的范式。

于 2008-12-09T04:09:17.983 回答
7

是的你可以。在 C++ 或Objective-C出现之前,人们就在编写面向对象的 C。C++ 和 Objective-C 都部分地尝试采用 C 中使用的一些 OO 概念并将它们形式化为语言的一部分。

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

#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;
}
于 2008-12-09T04:17:14.677 回答
6

当然,它不会像使用具有内置支持的语言那么漂亮。我什至写过“面向对象的汇编程序”。

于 2009-01-06T18:51:37.020 回答
6

如果您确信 OOP 方法更适合您要解决的问题,为什么还要尝试使用非 OOP 语言来解决它?似乎您使用了错误的工具来完成这项工作。使用 C++ 或其他一些面向对象的 C 变体语言。

如果您之所以问,是因为您开始在一个已经存在的用 C 编写的大型项目上进行编码,那么您不应该尝试将您自己(或其他任何人)的 OOP 范例强加到项目的基础架构中。遵循项目中已经存在的指导方针。一般来说,干净的 API 和独立的库和模块将大大有助于实现干净的 OOP 式设计

如果,毕竟,你真的准备做 OOP C,阅读这个(PDF)。

于 2010-04-28T19:43:35.487 回答
6

添加一点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;
}
于 2011-08-31T21:18:59.287 回答
5

我已经研究了一年:

由于 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,但是中文的文档要好得多)。

OOC-GCC

于 2011-12-05T12:02:27.227 回答
4

Jim Larson 1996 年在第 312 节编程午餐时间研讨会上发表的演讲中有一个使用 C 继承的示例:High and Low-Level C。

于 2008-12-09T04:12:48.697 回答
4

您可能想要做的一件事是研究Xt工具包的X Window的实现。当然,它正在变得越来越长,但是使用的许多结构被设计为在传统 C 中以 OO 方式工作。通常这意味着在这里和那里添加额外的间接层,并设计结构以相互叠加。

你真的可以用这种方式在 C 中以 OO 的方式做很多事情,即使有时感觉像这样,OO 概念并没有完全从#include<favorite_OO_Guru.h>. 它们确实构成了当时许多既定的最佳实践。OO 语言和系统只是提炼和放大了当时编程时代精神的一部分。

于 2010-04-28T21:17:23.433 回答
4

哪些文章或书籍适合在 C 中使用 OOP 概念?

Dave Hanson 的C Interfaces and Implementations在封装和命名方面非常出色,在函数指针的使用方面也非常出色Dave 不尝试模拟继承。

于 2010-04-29T02:38:33.617 回答
4

OOP 只是一种将数据置于程序中比代码更重要的范式。OOP 不是一种语言。因此,就像纯 C 是一门简单的语言一样,纯 C 中的 OOP 也很简单。

于 2012-01-15T23:50:59.277 回答
4

问题的答案是“是的,你可以”。

面向对象的 C (OOC) 工具包适用于那些想要以面向对象的方式编程,但也坚持使用良好的旧 C 语言的人。OOC 实现类、单继承和多继承、异常处理。

特征

• 仅使用C 宏和函数,无需语言扩展!(ANSI-C)

• 易于阅读的应用程序源代码。小心翼翼地使事情尽可能简单。

• 类的单一继承

• 接口和mixin 的多重继承(从1.3 版开始)

• 实现异常(在纯 C 中!)

• 类的虚函数

• 便于类实施的外部工具

有关详细信息,请访问http://ooc-coding.sourceforge.net/

于 2012-03-27T08:27:21.633 回答
4

似乎人们正在尝试使用 C 来模拟 C++ 风格。我的看法是,进行面向对象编程 C 确实是在进行面向结构的编程。但是,您可以实现后期绑定、封装和继承等功能。对于继承,您在子结构中显式定义指向基本结构的指针,这显然是多重继承的一种形式。您还需要确定您的

//private_class.h
struct private_class;
extern struct private_class * new_private_class();
extern int ret_a_value(struct private_class *, int a, int b);
extern void delete_private_class(struct private_class *);
void (*late_bind_function)(struct private_class *p);

//private_class.c
struct inherited_class_1;
struct inherited_class_2;

struct private_class {
  int a;
  int b;
  struct inherited_class_1 *p1;
  struct inherited_class_2 *p2;
};

struct inherited_class_1 * new_inherited_class_1();
struct inherited_class_2 * new_inherited_class_2();

struct private_class * new_private_class() {
  struct private_class *p;
  p = (struct private_class*) malloc(sizeof(struct private_class));
  p->a = 0;
  p->b = 0;
  p->p1 = new_inherited_class_1();
  p->p2 = new_inherited_class_2();
  return p;
}

    int ret_a_value(struct private_class *p, int a, int b) {
      return p->a + p->b + a + b;
    }

    void delete_private_class(struct private_class *p) {
      //release any resources
      //call delete methods for inherited classes
      free(p);
    }
    //main.c
    struct private_class *p;
    p = new_private_class();
    late_bind_function = &implementation_function;
    delete_private_class(p);

编译c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj

所以建议是坚持纯 C 风格,而不是试图强行进入 C++ 风格。此外,这种方式也适用于构建 API 的非常干净的方式。

于 2014-11-05T20:10:46.167 回答
2

请参阅http://slkpg.byethost7.com/instance.html以了解 C 中 OOP 的另一个转折点。它强调仅使用本机 C 可重入的实例数据。多重继承是使用函数包装器手动完成的。保持类型安全。这是一个小样本:

typedef struct _peeker
{
    log_t     *log;
    symbols_t *sym;
    scanner_t  scan;            // inherited instance
    peek_t     pk;
    int        trace;

    void    (*push) ( SELF *d, symbol_t *symbol );
    short   (*peek) ( SELF *d, int level );
    short   (*get)  ( SELF *d );
    int     (*get_line_number) ( SELF *d );

} peeker_t, SlkToken;

#define push(self,a)            (*self).push(self, a)
#define peek(self,a)            (*self).peek(self, a)
#define get(self)               (*self).get(self)
#define get_line_number(self)   (*self).get_line_number(self)

INSTANCE_METHOD
int
(get_line_number) ( peeker_t *d )
{
    return  d->scan.line_number;
}

PUBLIC
void
InitializePeeker ( peeker_t  *peeker,
                   int        trace,
                   symbols_t *symbols,
                   log_t     *log,
                   list_t    *list )
{
    InitializeScanner ( &peeker->scan, trace, symbols, log, list );
    peeker->log = log;
    peeker->sym = symbols;
    peeker->pk.current = peeker->pk.buffer;
    peeker->pk.count = 0;
    peeker->trace = trace;

    peeker->get_line_number = get_line_number;
    peeker->push = push;
    peeker->get = get;
    peeker->peek = peek;
}
于 2012-07-05T18:21:29.310 回答
2

我参加聚会有点晚了,但我想分享我在这个话题上的经验:这些天我使用嵌入式东西,我唯一(可靠)的编译器是 C,所以我想应用面向对象我用 C 编写的嵌入式项目中的方法。

到目前为止,我看到的大多数解决方案都大量使用类型转换,因此我们失去了类型安全性:如果你犯了错误,编译器将无济于事。这是完全不能接受的。

我有的要求:

  • 尽可能避免类型转换,这样我们就不会失去类型安全;
  • 多态性:我们应该能够使用虚方法,并且类的用户不应该知道某些特定方法是否是虚方法;
  • 多重继承:我不经常使用它,但有时我真的希望某个类实现多个接口(或扩展多个超类)。

我在这篇文章中详细解释了我的方法:C 中的面向对象编程;另外,还有一个用于为基类和派生类自动生成样板代码的实用程序。

于 2015-03-19T01:10:54.430 回答
2

我建立了一个小图书馆,在那里我尝试过,对我来说它工作得很好。所以我想我分享一下经验。

https://github.com/thomasfuhringer/oxygen

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

如前所述,虚方法有些棘手。但它们是可行的。为了简单起见,我只是在类描述结构中使用了一组函数,每个子类在需要时复制并重新填充各个插槽。

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

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

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

这一切都非常简单和精简,并为我提供了 OO 的基本要素,而无需强迫我处理 C++ 这个怪物。而且我保留了留在 C 领域的灵活性,这使得集成第三方库变得更加容易。

于 2016-03-02T08:38:39.817 回答
1

我建议使用 Objective-C,它是 C 的超集。

虽然 Objective-C 已有 30 年的历史,但它允许编写优雅的代码。

http://en.wikipedia.org/wiki/Objective-C

于 2010-08-27T22:42:13.957 回答
1

是的,有可能。

这是纯 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
*/
于 2012-07-24T10:05:25.717 回答
0

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

于 2008-12-09T04:06:03.843 回答
0

我认为首先要说的是(至少恕我直言)C 的函数指针实现真的很难使用。我会跳过一大堆箍来避免函数指针......

就是说,我觉得别人说的还不错。你有结构,你有模块,而不是foo->method(a,b,c),你最终会得到method(foo,a,b,c) 如果你有多个类型的“方法”方法,那么你可以在它前面加上类型,所以FOO_method(foo,a,b,c),正如其他人所说......很好用.h 文件,您可以获得私有和公共等。

现在,有一些东西是这种技术不会给你的。它不会为您提供私有数据字段。我认为,这与意志力和良好的编码卫生有关......而且,没有一种简单的方法可以用它进行继承。

这些至少是简单的部分……其余的,我认为是90/10的情况。10% 的收益将需要 90% 的工作...

于 2010-04-28T21:37:54.513 回答