Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/github/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C语言中的面向对象_C_Oop_Object - Fatal编程技术网

C语言中的面向对象

C语言中的面向对象,c,oop,object,C,Oop,Object,什么是一组漂亮的预处理器黑客(ANSI C89/ISO C90兼容),能够在C中实现某种丑陋(但可用)的对象定向 我熟悉一些不同的面向对象语言,所以请不要回答“学习C++”这样的答案。我读过“”(注意:PDF格式)和其他一些有趣的解决方案,但我最感兴趣的是你的:-) 另请参见我曾经使用过一个C库,它的实现方式让我觉得非常优雅。他们在C中编写了一种定义对象的方法,然后继承它们,使它们作为C++对象可扩展。其基本思想是: struct vehicle { int power; int

什么是一组漂亮的预处理器黑客(ANSI C89/ISO C90兼容),能够在C中实现某种丑陋(但可用)的对象定向

我熟悉一些不同的面向对象语言,所以请不要回答“学习C++”这样的答案。我读过“”(注意:PDF格式)和其他一些有趣的解决方案,但我最感兴趣的是你的:-)



另请参见

我曾经使用过一个C库,它的实现方式让我觉得非常优雅。他们在C中编写了一种定义对象的方法,然后继承它们,使它们作为C++对象可扩展。其基本思想是:

struct vehicle {
   int power;
   int weight;
}
  • 每个对象都有自己的文件
  • 公共函数和变量在对象的.h文件中定义
  • 私有变量和函数仅位于.c文件中
  • 要“继承”,将创建一个新结构,该结构的第一个成员是要从中继承的对象
继承很难描述,但基本上是这样的:

struct vehicle {
   int power;
   int weight;
}
然后在另一个文件中:

struct van {
   struct vehicle base;
   int cubic_size;
}
然后,您可以在内存中创建一辆面包车,并由只了解车辆的代码使用:

struct van my_van;
struct vehicle *something = &my_van;
vehicle_function( something );

它工作得很好,.h文件精确地定义了您应该能够对每个对象执行的操作。

(用于视频处理的工具包)是用纯C(和汇编语言)编写的,但使用的是面向对象的风格。它充满了带函数指针的结构。有一组工厂函数使用适当的“方法”指针初始化结构

如果将对象上调用的方法视为静态方法,将一个隐式“
this
”传递到函数中,则可以更轻松地在C中思考OO

例如:

String s = "hi";
System.out.println(s.length());
变成:

string s = "hi";
printf(length(s)); // pass in s, as an implicit this

或者类似的东西。

GNOME desktop for Linux是用面向对象的C编写的,它有一个名为“”的对象模型,它支持属性、继承、多态性,以及一些其他功能,如引用、事件处理(称为“信号”)、运行时类型、私有数据等

它包括一些预处理器的黑客操作,比如在类层次结构中进行类型转换,等等。下面是我为GNOME编写的一个示例类(比如gchar就是typedef):


在GObject结构中有一个GType整数,它被用作GLib的动态类型系统的一个神奇数字(你可以将整个结构转换为“GType”来找到它的类型)。

我建议不要使用预处理器(ab)来尝试使C语法更像另一种更面向对象的语言。在最基本的级别上,您只需使用普通结构作为对象,并通过指针传递它们:

struct monkey
{
    float age;
    bool is_male;
    int happiness;
};

void monkey_dance(struct monkey *monkey)
{
    /* do a little dance */
}
要获得像继承和多态性这样的东西,您必须更加努力。您可以通过将结构的第一个成员作为超类的实例来执行手动继承,然后可以自由转换指向基类和派生类的指针:

struct base
{
    /* base class members */
};

struct derived
{
    struct base super;
    /* derived class members */
};

struct derived d;
struct base *base_ptr = (struct base *)&d;  // upcast
struct derived *derived_ptr = (struct derived *)base_ptr;  // downcast
要获取多态性(即虚拟函数),可以使用函数指针和可选的函数指针表,也称为虚拟表或vtables:

struct base;
struct base_vtable
{
    void (*dance)(struct base *);
    void (*jump)(struct base *, int how_high);
};

struct base
{
    struct base_vtable *vtable;
    /* base members */
};

void base_dance(struct base *b)
{
    b->vtable->dance(b);
}

void base_jump(struct base *b, int how_high)
{
    b->vtable->jump(b, how_high);
}

struct derived1
{
    struct base super;
    /* derived1 members */
};

void derived1_dance(struct derived1 *d)
{
    /* implementation of derived1's dance function */
}

void derived1_jump(struct derived1 *d, int how_high)
{
    /* implementation of derived 1's jump function */
}

/* global vtable for derived1 */
struct base_vtable derived1_vtable =
{
    &derived1_dance, /* you might get a warning here about incompatible pointer types */
    &derived1_jump   /* you can ignore it, or perform a cast to get rid of it */
};

void derived1_init(struct derived1 *d)
{
    d->super.vtable = &derived1_vtable;
    /* init base members d->super.foo */
    /* init derived1 members d->foo */
}

struct derived2
{
    struct base super;
    /* derived2 members */
};

void derived2_dance(struct derived2 *d)
{
    /* implementation of derived2's dance function */
}

void derived2_jump(struct derived2 *d, int how_high)
{
    /* implementation of derived2's jump function */
}

struct base_vtable derived2_vtable =
{
   &derived2_dance,
   &derived2_jump
};

void derived2_init(struct derived2 *d)
{
    d->super.vtable = &derived2_vtable;
    /* init base members d->super.foo */
    /* init derived1 members d->foo */
}

int main(void)
{
    /* OK!  We're done with our declarations, now we can finally do some
       polymorphism in C */
    struct derived1 d1;
    derived1_init(&d1);

    struct derived2 d2;
    derived2_init(&d2);

    struct base *b1_ptr = (struct base *)&d1;
    struct base *b2_ptr = (struct base *)&d2;

    base_dance(b1_ptr);  /* calls derived1_dance */
    base_dance(b2_ptr);  /* calls derived2_dance */

    base_jump(b1_ptr, 42);  /* calls derived1_jump */
    base_jump(b2_ptr, 42);  /* calls derived2_jump */

    return 0;
}
这就是C语言中多态性的实现方式。它并不漂亮,但它确实起到了作用。基类和派生类之间的指针强制转换存在一些棘手的问题,只要基类是派生类的第一个成员,这是安全的。多重继承要困难得多——在这种情况下,为了在基类之间(而不是第一个基类之间)进行区分,您需要根据适当的偏移量手动调整指针,这非常棘手而且容易出错


您可以做的另一件(棘手的)事情是在运行时更改对象的动态类型!您只需重新分配一个新的vtable指针。您甚至可以有选择地更改某些虚拟函数,同时保留其他函数,从而创建新的混合类型。只需小心创建一个新的vtable,而不是修改全局vtable,否则您会意外地影响给定类型的所有对象。

在我知道OOP是什么之前,我曾经在C中做过这种事情

下面是一个示例,它实现了一个数据缓冲区,该缓冲区在给定最小大小、增量和最大大小的情况下根据需要增长。这个特定的实现是基于“元素”的,也就是说,它被设计为允许任何C类型的类似列表的集合,而不仅仅是一个可变长度的字节缓冲区

其思想是使用xxx_crt()实例化对象,并使用xxx_dlt()删除对象。每个“成员”方法都使用一个特定类型的指针进行操作

我以这种方式实现了一个链表、循环缓冲区和许多其他东西

我必须承认,我从未想过如何用这种方法实现继承。我想基维利所提供的一些混合可能是一条不错的道路

dtb.c:

#include <limits.h>
#include <string.h>
#include <stdlib.h>

static void dtb_xlt(void *dst, const void *src, vint len, const byte *tbl);

DTABUF *dtb_crt(vint minsiz,vint incsiz,vint maxsiz) {
    DTABUF          *dbp;

    if(!minsiz) { return NULL; }
    if(!incsiz)                  { incsiz=minsiz;        }
    if(!maxsiz || maxsiz<minsiz) { maxsiz=minsiz;        }
    if(minsiz+incsiz>maxsiz)     { incsiz=maxsiz-minsiz; }
    if((dbp=(DTABUF*)malloc(sizeof(*dbp))) == NULL) { return NULL; }
    memset(dbp,0,sizeof(*dbp));
    dbp->min=minsiz;
    dbp->inc=incsiz;
    dbp->max=maxsiz;
    dbp->siz=minsiz;
    dbp->cur=0;
    if((dbp->dta=(byte*)malloc((vuns)minsiz)) == NULL) { free(dbp); return NULL; }
    return dbp;
    }

DTABUF *dtb_dlt(DTABUF *dbp) {
    if(dbp) {
        free(dbp->dta);
        free(dbp);
        }
    return NULL;
    }

vint dtb_adddta(DTABUF *dbp,const byte *xlt256,const void *dtaptr,vint dtalen) {
    if(!dbp) { errno=EINVAL; return -1; }
    if(dtalen==-1) { dtalen=(vint)strlen((byte*)dtaptr); }
    if((dbp->cur + dtalen) > dbp->siz) {
        void        *newdta;
        vint        newsiz;

        if((dbp->siz+dbp->inc)>=(dbp->cur+dtalen)) { newsiz=dbp->siz+dbp->inc; }
        else                                       { newsiz=dbp->cur+dtalen;   }
        if(newsiz>dbp->max) { errno=ETRUNC; return -1; }
        if((newdta=realloc(dbp->dta,(vuns)newsiz))==NULL) { return -1; }
        dbp->dta=newdta; dbp->siz=newsiz;
        }
    if(dtalen) {
        if(xlt256) { dtb_xlt(((byte*)dbp->dta+dbp->cur),dtaptr,dtalen,xlt256); }
        else       { memcpy(((byte*)dbp->dta+dbp->cur),dtaptr,(vuns)dtalen);   }
        dbp->cur+=dtalen;
        }
    return 0;
    }

static void dtb_xlt(void *dst,const void *src,vint len,const byte *tbl) {
    byte            *sp,*dp;

    for(sp=(byte*)src,dp=(byte*)dst; len; len--,sp++,dp++) { *dp=tbl[*sp]; }
    }

vint dtb_addtxt(DTABUF *dbp,const byte *xlt256,const byte *format,...) {
    byte            textÝ501¨;
    va_list         ap;
    vint            len;

    va_start(ap,format); len=sprintf_len(format,ap)-1; va_end(ap);
    if(len<0 || len>=sizeof(text)) { sprintf_safe(text,sizeof(text),"STRTOOLNG: %s",format); len=(int)strlen(text); }
    else                           { va_start(ap,format); vsprintf(text,format,ap); va_end(ap);                     }
    return dtb_adddta(dbp,xlt256,text,len);
    }

vint dtb_rmvdta(DTABUF *dbp,vint len) {
    if(!dbp) { errno=EINVAL; return -1; }
    if(len > dbp->cur) { len=dbp->cur; }
    dbp->cur-=len;
    return 0;
    }

vint dtb_reset(DTABUF *dbp) {
    if(!dbp) { errno=EINVAL; return -1; }
    dbp->cur=0;
    if(dbp->siz > dbp->min) {
        byte *newdta;
        if((newdta=(byte*)realloc(dbp->dta,(vuns)dbp->min))==NULL) {
            free(dbp->dta); dbp->dta=null; dbp->siz=0;
            return -1;
            }
        dbp->dta=newdta; dbp->siz=dbp->min;
        }
    return 0;
    }

void *dtb_elmptr(DTABUF *dbp,vint elmidx,vint elmlen) {
    if(!elmlen || (elmidx*elmlen)>=dbp->cur) { return NULL; }
    return ((byte*)dbp->dta+(elmidx*elmlen));
    }

PS:vint只是int的一个typedef-我用它来提醒我,它的长度随平台的不同而变化(用于移植)。

对于我来说,C中的面向对象应该具有以下特性:

  • 封装和数据隐藏(可以使用结构/不透明指针实现)

  • 继承和对多态性的支持(可以使用结构实现单个继承-确保抽象基不可实例化)

  • 构造函数和析构函数功能(不容易实现)

  • 类型检查(至少针对用户定义的类型,因为C不强制任何类型)

  • 引用计数(或要实现的东西)

  • 对异常处理的支持有限(setjmp和longjmp)

  • 除此之外,它应该依赖于ANSI/ISO规范,而不应该依赖于特定于编译器的功能。

    听起来很有希望(它仍然是alpha版本)。为了简单和灵活,它试图将可用的概念保持在最低限度:统一的面向对象编程,包括开放类、元
    #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);
    }
    
    6.56
    13.12
    
    typedef void *Class;
    
    typedef struct __class_Foo
    {
        Class isa;
        int ivar;
    } Foo;
    
    typedef struct __meta_Foo
    {
        Foo *(*alloc)(void);
        Foo *(*init)(Foo *self);
        int (*ivar)(Foo *self);
        void (*setIvar)(Foo *self);
    } meta_Foo;
    
    meta_Foo *class_Foo;
    
    void __meta_Foo_init(void) __attribute__((constructor));
    void __meta_Foo_init(void)
    {
        class_Foo = malloc(sizeof(meta_Foo));
        if (class_Foo)
        {
            class_Foo = {__imp_Foo_alloc, __imp_Foo_init, __imp_Foo_ivar, __imp_Foo_setIvar};
        }
    }
    
    Foo *__imp_Foo_alloc(void)
    {
        Foo *foo = malloc(sizeof(Foo));
        if (foo)
        {
            memset(foo, 0, sizeof(Foo));
            foo->isa = class_Foo;
        }
        return foo;
    }
    
    Foo *__imp_Foo_init(Foo *self)
    {
        if (self)
        {
            self->ivar = 42;
        }
        return self;
    }
    // ...
    
    int main(void)
    {
        Foo *foo = (class_Foo->init)((class_Foo->alloc)());
        printf("%d\n", (foo->isa->ivar)(foo)); // 42
        foo->isa->setIvar(foo, 60);
        printf("%d\n", (foo->isa->ivar)(foo)); // 60
        free(foo);
    }
    
    @interface Foo : NSObject
    {
        int ivar;
    }
    - (int)ivar;
    - (void)setIvar:(int)ivar;
    @end
    
    @implementation Foo
    - (id)init
    {
        if (self = [super init])
        {
            ivar = 42;
        }
        return self;
    }
    @end
    
    int main(void)
    {
        Foo *foo = [[Foo alloc] init];
        printf("%d\n", [foo ivar]);
        [foo setIvar:60];
        printf("%d\n", [foo ivar]);
        [foo release];
    }
    
    GHastTable* my_hash = g_hash_table_new(g_str_hash, g_str_equal);
    int size = g_hash_table_size(my_hash);
    ...
    
    g_hash_table_remove(my_hash, some_key);
    
    //monkey.h
    
        struct _monkey;
    
        typedef struct _monkey monkey;
    
        //memory management
        monkey * monkey_new();
        int monkey_delete(monkey *thisobj);
        //methods
        void monkey_dance(monkey *thisobj);
    
    /*
     * OOP in C
     *
     * gcc -o oop oop.c
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    struct obj2d {
        float x;                            // object center x
        float y;                            // object center y
        float (* area)(void *);
    };
    
    #define X(obj)          (obj)->b1.x
    #define Y(obj)          (obj)->b1.y
    #define AREA(obj)       (obj)->b1.area(obj)
    
    void *
    _new_obj2d(int size, void * areafn)
    {
        struct obj2d * x = calloc(1, size);
        x->area = areafn;
        // obj2d constructor code ...
        return x;
    }
    
    // --------------------------------------------------------
    
    struct rectangle {
        struct obj2d b1;        // base class
        float width;
        float height;
        float rotation;
    };
    
    #define WIDTH(obj)      (obj)->width
    #define HEIGHT(obj)     (obj)->height
    
    float rectangle_area(struct rectangle * self)
    {
        return self->width * self->height;
    }
    
    #define NEW_rectangle()  _new_obj2d(sizeof(struct rectangle), rectangle_area)
    
    // --------------------------------------------------------
    
    struct triangle {
        struct obj2d b1;
        // deliberately unfinished to test error messages
    };
    
    #define NEW_triangle()  _new_obj2d(sizeof(struct triangle), triangle_area)
    
    // --------------------------------------------------------
    
    struct circle {
        struct obj2d b1;
        float radius;
    };
    
    #define RADIUS(obj)     (obj)->radius
    
    float circle_area(struct circle * self)
    {
        return M_PI * self->radius * self->radius;
    }
    
    #define NEW_circle()     _new_obj2d(sizeof(struct circle), circle_area)
    
    // --------------------------------------------------------
    
    #define NEW(objname)            (struct objname *) NEW_##objname()
    
    
    int
    main(int ac, char * av[])
    {
        struct rectangle * obj1 = NEW(rectangle);
        struct circle    * obj2 = NEW(circle);
    
        X(obj1) = 1;
        Y(obj1) = 1;
    
        // your decision as to which of these is clearer, but note above that
        // macros also hide the fact that a member is in the base class
    
        WIDTH(obj1)  = 2;
        obj1->height = 3;
    
        printf("obj1 position (%f,%f) area %f\n", X(obj1), Y(obj1), AREA(obj1));
    
        X(obj2) = 10;
        Y(obj2) = 10;
        RADIUS(obj2) = 1.5;
        printf("obj2 position (%f,%f) area %f\n", X(obj2), Y(obj2), AREA(obj2));
    
        // WIDTH(obj2)  = 2;                                // error: struct circle has no member named width
        // struct triangle  * obj3 = NEW(triangle);         // error: triangle_area undefined
    }
    
    #include "class-framework.h"
    
    CLASS (People) {
        int age;
    };
    
    int main()
    {
        People *p = NEW (People);
    
        p->age = 10;
    
        printf("%d\n", p->age);
    }
    
    #define CLASS Point
    #define BUILD_JSON
    
    #define Point__define                            \
        METHOD(Point,public,int,move_up,(int steps)) \
        METHOD(Point,public,void,draw)               \
                                                     \
        VAR(read,int,x,JSON(json_int))               \
        VAR(read,int,y,JSON(json_int))               \
    
    METHOD(Point,public,void,draw)
    {
        printf("point at %d,%d\n", self->x, self->y);
    }
    
    class Point
        public int move_up(int steps)
        public void draw()
        read int x
        read int y