C语言中的面向对象编程

C语言中的面向对象编程,c,oop,C,Oop,可能的重复项: 我记得以前读过关于某人(我想是Linus Torvalds)的文章,讨论C++是一种可怕的语言,以及如何用C.编写面向对象程序,因为我有时间来思考,我真的不知道所有面向对象的概念如何被继承到C中。有些事情是相当明显的。例如: 要模拟成员函数,可以在结构中放置函数指针 要模拟多态性,您可以编写一个函数,该函数接受可变数量的参数,并根据参数(例如,sizeofs)执行一些巫术 但是,您将如何模拟封装和继承 我认为封装可以通过一个存储私有成员的嵌套结构来模拟。它很容易找到,但可能会

可能的重复项:

我记得以前读过关于某人(我想是Linus Torvalds)的文章,讨论C++是一种可怕的语言,以及如何用C.编写面向对象程序,因为我有时间来思考,我真的不知道所有面向对象的概念如何被继承到C中。有些事情是相当明显的。例如:

  • 要模拟成员函数,可以在结构中放置函数指针
  • 要模拟多态性,您可以编写一个函数,该函数接受可变数量的参数,并根据参数(例如,
    sizeof
    s)执行一些巫术
  • 但是,您将如何模拟封装和继承


    我认为封装可以通过一个存储私有成员的嵌套结构来模拟。它很容易找到,但可能会被命名为
    PRIVATE
    或其他同样明显的名称,以表明它不打算从结构外部使用。那么继承呢?

    gtk和glib库使用宏将对象强制转换为各种类型。
    添加_小部件(GTK_小部件(myButton))

    我不能说它是如何做到的,但你可以阅读他们的来源,以了解它是如何做到的

    关于C语言中面向对象编程的一个很好的例子,请看几年前的POV-Ray源代码——3.1g版本特别好。当然,“对象”是带有函数指针的结构。宏用于为抽象对象提供核心方法和数据,派生类是从该宏开始的结构。然而,没有人试图与私人/公共机构打交道。要看到的东西是在.h文件中,实现细节主要在.c文件中,除了许多例外

    <> P>有一些巧妙的技巧,我不知道如何才能被移植到C++中,比如通过重新分配函数指针,将一个类转换成不同但类似的一个类。对于今天的动态语言来说很简单。我忘了细节;我想可能是CSG交叉点和联合对象


    你读过关于这个主题的《圣经》吗?请看…

    一段有趣的历史,最初的C++实现输出C代码,然后需要一个C编译器来实际编译最终代码。因此,任何可以用C++表示的东西都可以写成C.< /P> < P>。下面是我为编程练习而发明的一个非常简洁的系统(基于C++):

    构造函数分配内存,然后调用初始化内存的类的init函数。每个init函数还应该包含一个静态vtable结构,其中包含虚拟函数指针(对于纯虚拟函数,为NULL)。派生类init函数在执行任何其他操作之前调用超类init函数

    通过实现虚拟函数包装器(不要与vtables指向的函数混淆),可以创建一个非常好的API,如下所示(如果在标题中这样做,则在其前面添加
    静态内联
    ):

    单一继承可以通过滥用结构的二进制布局来实现:

    请注意,多重继承比较混乱,因为在层次结构类型之间转换时,通常需要调整指针值

    也可以将其他特定于类型的数据添加到虚拟表中。示例包括运行时类型信息(例如,作为字符串的类型名称),链接到超类vtable和析构函数链。您可能需要虚拟析构函数,其中派生类析构函数将对象降级为其超类,然后递归调用该类的析构函数,依此类推,直到到达基类析构函数,最终释放结构


    封装是通过在player_protected.h中定义结构并在player_protected.c中实现函数(由vtable指向)来完成的,类似地,对于派生类也是如此,但这相当笨拙,并且会降低性能(因为虚拟包装器无法放入头中),所以我建议不要这样做。

    处理继承的一种方法是使用嵌套结构:

    struct base
    {
        ...
    };
    
    void method_of_base(base *b, ...);
    
    struct child
    {
        struct base base_elements;
        ...
    };
    
    然后,您可以执行以下操作:

    struct child c;
    method_of_base(&c.b, ...);
    
    但是,您将如何模拟封装和继承

    实际上,封装是最简单的部分。封装是一种设计理念,它与语言无关,与您如何思考问题无关

    例如,Windows文件api是完全封装的。打开文件时,会返回一个不透明对象,该对象包含文件“object”的所有状态信息。将此句柄交回每个文件io API。封装实际上比C++好得多,因为没有公共头文件,人们可以查看并查看私有变量的名称。 继承比较困难,但要使代码成为面向对象的代码,根本不需要继承。在某些方面,聚合比继承更好,在C中聚合与C++一样简单。例如,见

    作为对Neil的回应,请参阅,以了解为什么多态性不需要继承


    美国老程序员在C++编译器可用之前编写了面向对象代码,它是一个思维工具,而不是一个工具集。

    < P>苹果的基于C的核心框架是实际编写的,以便它的“对象”可以在Objto-C中作为对象的双重语言。该框架的很大一部分在苹果网站上是开源的。在这样做的主要操作系统级框架中,这可能是一个有用的案例研究。

    您可能想看看Objective-C,它就是这么做的。它只是一个将Objective-Coo代码编译成C的前端。

    看看VFS层在Linux内核中的工作方式,以获得一个继承模式的示例。文件操作为th
    struct child c;
    method_of_base(&c.b, ...);
    
    struct T
    {
       int data;
       int get_data() const { return data; }
    };
    
    typedef struct objc_object {
        Class isa;
    } *id;
    
    typedef struct objc_class {
        struct objc_class *isa;
        struct objc_class *super_class
        const char *name;
        long version;
        long info
        long instance_size;
        struct objc_ivar_list *ivars;
        struct objc_method_list **methodLists;
       struct objc_cache *cache;
       struct objc_protocol_list *protocols;
    } *Class;