سؤال

ما يمكن أن يكون مجموعة من أنيق المعالج الخارقة (ANSI C89/ISO C90 متوافق) التي تمكن نوع من القبيح (ولكن صالحة للاستعمال) كائن التوجه في C ؟

وأنا على دراية عدة وجوه المنحى لغة ، لذا يرجى لا تستجيب مع إجابات مثل "تعلم C++!".لقد قرأت "وجوه المنحى البرمجة مع ANSI C"(حذار: شكل PDF) و عدة حلول أخرى مثيرة للاهتمام, ولكن أنا في الغالب مهتم لك :-)!


انظر أيضا يمكنك كتابة وجوه المنحى البرمجية في C ؟

هل كانت مفيدة؟

المحلول

ج وجوه النظام (كوس) أصوات واعدة (انها لا تزال في النسخة ألفا).فإنه يحاول الحفاظ على الحد الأدنى المتاحة المفاهيم من أجل البساطة والمرونة:موحدة وجوه المنحى البرمجة بما في ذلك الطبقات ، metaclasses الملكية metaclasses ، الوراثة ، multimethods, وفد, ملكية, استثناءات, العقود و الإغلاق.هناك مشروع الورق (PDF) التي توضح ذلك.

استثناء في ج هو C89 تنفيذ المحاولة-CATCH-وأخيرا وجدت في لغات أخرى OO.لأنه يأتي مع testsuite بعض الأمثلة.

كل من لوران دنيو التي يعمل الكثير على OOP في ج.

نصائح أخرى

أنصح ضد المعالج (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 وإلا بطريق الخطأ تؤثر على جميع الكائنات من نوع معين.

لقد عملت مع ج المكتبة التي تم تنفيذها في الطريقة التي تصورت أنها أنيقة جدا.وكان قد كتب ، ج ، وسيلة لتحديد الكائنات ، ثم ترث من لهم حتى أنهم كانوا كما الموسعة C++ الكائن.كانت الفكرة الأساسية في هذا:

  • كل كائن لديه ملف خاص به
  • وظائف عمومية المتغيرات المحددة في .ساعة الملف لكائن
  • خاصة المتغيرات والدوال التي كانت تقع فقط في .ج الملف
  • أن "يرث" جديد struct يتم إنشاؤه مع أول عضو من البنية كونها كائن يرث من

وراثة من الصعب وصف ، ولكن الأساس هو هذا:

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

انها عملت بشكل جميل،.ساعة ملفات تعريف بالضبط ما يجب أن تكون قادرة على القيام مع كل كائن.

جنوم سطح المكتب لينكس هو مكتوب في وجوه المنحى ج ، وقد كائن نموذج يسمى "GObject"الذي يدعم خصائص الميراث والتعدد ، فضلا عن بعض الأشياء الجيدة الأخرى مثل المراجع التعامل مع الحدث (يسمى "إشارات"), وقت كتابة البيانات الخاصة ، إلخ.

ويشمل المعالج الخارقة أن تفعل أشياء مثل بتلبيس حولها في التسلسل الهرمي فئة ، إلخ.هنا مثال على الدرجة كتبت غنوم (أشياء مثل gchar هي typedefs):

صنف المصدر

فئة رأس

داخل GObject هيكل هناك GType عدد صحيح التي تستخدم السحر رقم سطحي الديناميكي نظام الكتابة (يمكنك يلقي كامل البنية إلى "GType" للعثور على انها نوع).

إذا كنت تعتقد أن من أساليب يطلق على الأجسام الساكنة الأساليب التي تمر ضمني 'this'في وظيفة يمكن أن تجعل التفكير OO في ج أسهل.

على سبيل المثال:

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

يصبح:

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

أو شيء من هذا القبيل.

قليلا خارج الموضوع, ولكن الأصل C++ مترجم ، Cfront, جمعت C++ C ومن ثم إلى المجمع.

الحفاظ هنا.

اعتدت أن تفعل هذا النوع من الشيء في C ، قبل أن أعرف ما OOP كان.

التالي هو مثال ، التي تطبق على البيانات العازلة التي تنمو على الطلب ، وبالنظر إلى الحد الأدنى من حجم الزيادة الحد الأقصى للحجم.هذا خاص التنفيذ "عنصر" بناء ، وهو يقول تم تصميمه للسماح قائمة-مثل جمع أي نوع C ، وليس مجرد طول متغير بايت العازلة.

الفكرة هي أن الكائن مثيل باستخدام xxx_crt() و المحذوفة باستخدام xxx_dlt().كل من "الأعضاء" أساليب يأخذ على وجه التحديد كتبته المؤشر أن تعمل على.

لقد نفذت قائمة مرتبطة ، دوري العازلة ، وعدد من الأشياء الأخرى في هذه الطريقة.

يجب أن أعترف ، لم يسبق لي أن فكرت في كيفية تنفيذ الميراث مع هذا النهج.أتصور أن بعض مزيج من تلك التي تقدمها Kieveli قد يكون مسار جيد.

dtb.ج:

#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));
    }

dtb.ح

typedef _Packed struct {
    vint            min;                /* initial size                       */
    vint            inc;                /* increment size                     */
    vint            max;                /* maximum size                       */
    vint            siz;                /* current size                       */
    vint            cur;                /* current data length                */
    void            *dta;               /* data pointer                       */
    } DTABUF;

#define dtb_dtaptr(mDBP)                (mDBP->dta)
#define dtb_dtalen(mDBP)                (mDBP->cur)

DTABUF              *dtb_crt(vint minsiz,vint incsiz,vint maxsiz);
DTABUF              *dtb_dlt(DTABUF *dbp);
vint                dtb_adddta(DTABUF *dbp,const byte *xlt256,const void *dtaptr,vint dtalen);
vint                dtb_addtxt(DTABUF *dbp,const byte *xlt256,const byte *format,...);
vint                dtb_rmvdta(DTABUF *dbp,vint len);
vint                dtb_reset(DTABUF *dbp);
void                *dtb_elmptr(DTABUF *dbp,vint elmidx,vint elmlen);

PS:فينت كان مجرد typedef من int - اعتدت عليه أن يذكر لي أنه كان طول متغير من منصة الى منصة (تحويل).

ffmpeg (مجموعة أدوات معالجة الفيديو) هو مكتوب في مباشرة ج (و لغة التجميع) ، ولكن باستخدام كائن المنحى النمط.انها كاملة من البنيات مع مؤشرات الدالة.هناك مجموعة من مصنع وظائف تهيئة البنيات الملائمة "طريقة" المؤشرات.

إذا كنت تعتقد حقا catefully حتى القياسية ج استخدام المكتبة OOP - النظر في FILE * كمثال: fopen() تهيئة أحد FILE * الكائن استخدامه استخدام طرق الأعضاء fscanf(), fprintf(), fread(), fwrite() و غيرها, و في نهاية المطاف وضع اللمسات الأخيرة عليه مع fclose().

يمكنك أيضا الذهاب مع الزائفة الهدف-C الطريق التي ليس من الصعب أيضا:

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

هذا هو ما قد يكون ناتجا عن بعض الهدف-C مثل هذا ، إذا كانت جميلة عمرها الهدف-ج-ل-ج مترجم المستخدمة:

@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];
}

أعتقد أن ما آدم روزنفيلد نشرت هي الطريقة الصحيحة للقيام OOP في C.أود أن أضيف أن ما يظهر هو تنفيذ الكائن.وبعبارة أخرى التنفيذ الفعلي سيتم وضعها في .c ملف, في حين أن واجهة سيكون وضع في رأس .h الملف.على سبيل المثال باستخدام قرد المثال أعلاه:

واجهة تبدو مثل:

//monkey.h

    struct _monkey;

    typedef struct _monkey monkey;

    //memory management
    monkey * monkey_new();
    int monkey_delete(monkey *thisobj);
    //methods
    void monkey_dance(monkey *thisobj);

يمكنك أن ترى في واجهة .h الملف يمكنك فقط تحديد النماذج.ثم يمكنك تجميع تنفيذ جزء" .c ملف" في ثابت أو مكتبة ديناميكية.وهذا يخلق التغليف و أيضا يمكنك تغيير التنفيذ في الإرادة.المستخدم من وجوه الخاص بك يحتاج إلى أن يعرف شيئا تقريبا عن تنفيذ ذلك.هذا أيضا أماكن تركز على التصميم العام من وجوه.

هو اعتقادي الشخصي أن oop هو وسيلة لتصور الخاص بك هيكل رمز إعادة استخدام و له حقا أن تفعل شيئا مع تلك الأشياء الأخرى التي تضاف إلى c++ مثل الحمولة الزائدة أو قوالب.نعم تلك هي لطيفة جدا ميزات مفيدة ولكنها ليست من وجوه المنحى البرمجة حقا.

توصيتي:يبقيه بسيط.واحدة من أكبر القضايا لدي هو الحفاظ على البرامج القديمة (في بعض الأحيان أكثر من 10 سنوات من العمر).إذا كان رمز ليست بسيطة ، يمكن أن يكون من الصعب.نعم ، يمكن للمرء أن الكتابة مفيدة جدا OOP مع تعدد الأشكال في C ، ولكن يمكن أن يكون من الصعب قراءة.

أنا أفضل الأشياء البسيطة التي تغلف بعض المعالم وظائف.مثال عظيم على هذا GLIB2, على سبيل المثال تجزئة الجدول:

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

المفاتيح:

  1. بسيطة الهندسة المعمارية و تصميم نمط
  2. يحقق الأساسية OOP التغليف.
  3. سهلة التنفيذ ، قراءة وفهم ، والحفاظ على

إذا كنت تنوي كتابة OOP في ج أود أن أذهب مع الزائفةPimpl تصميم.بدلا من تمرير مؤشرات إلى البنيات ، كنت في نهاية المطاف تمرير المؤشرات إلى مؤشرات إلى البنيات.وهذا ما يجعل المحتوى مبهمة و يسهل تعدد الأشكال و الميراث.

المشكلة الحقيقية مع OOP في C هو ما يحدث عندما المتغيرات الخروج من نطاق.لا توجد مترجم ولدت المتلفات وهذا يمكن أن يسبب مشاكل. وحدات الماكرو ربما يمكن أن تساعد ولكن هو دائما ما يكون قبيح أن ننظر إلى.

طريقة أخرى أن البرنامج في وجوه المنحى الاسلوب مع ج هو استخدام مولد رمز الذي يحول المجال محددة لغة C.كما فعلت مع نسخة مطبوعة على الآلة الكاتبة جافا سكريبت جلب OOP js.

#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

هنا هو إظهار ما هو OO البرمجة C.

هذا هو الحقيقي الخالص ، ج ، لا preprocessor وحدات الماكرو.لدينا الميراث ، تعدد الأشكال و تغليف البيانات (بما في ذلك البيانات الخاصة إلى فئات أو الكائنات).لا توجد فرصة المحمية تصفيات ما يعادلها ، وهذا هو ، خاصة البيانات الخاصة أسفل innheritance سلسلة جدا.ولكن هذا ليس مصدر إزعاج لأنني لا أعتقد أنه من الضروري.

CPolygon لا مثيل لأننا لا نستخدمها إلا أن التعامل مع الكائنات من أسفل innheritance السلسلة التي لها جوانب مشتركة ولكن مختلفة تنفيذها (تعدد الأشكال).

@آدم روزنفيلد جيد جدا شرح كيفية تحقيق OOP مع ج

الى جانب ذلك, أنصح لك أن تقرأ

1) pjsip

جيد جدا ج مكتبة الصوت عبر بروتوكول الإنترنت.يمكنك أن تتعلم كيف يحقق OOP على الرغم من البنيات وظيفة مؤشر الجداول

2) وقت التشغيل دائرة الرقابة الداخلية

تعلم كيف دائرة الرقابة الداخلية تشغيل القوى الهدف C.فإنه يحقق OOP من خلال مؤشر عيسى ميتا الدرجة

بالنسبة لي كائن التوجه في C يجب أن تكون هذه الميزات:

  1. التغليف و إخفاء البيانات (يمكن تحقيقه باستخدام البنيات/مبهمة المؤشرات)

  2. الميراث و دعم تعدد الأشكال (الميراث واحد يمكن تحقيقه باستخدام البنيات - تأكد مجردة القاعدة ليست instantiable)

  3. منشئ والمدمر وظيفة (ليس من السهل تحقيق)

  4. نوع التدقيق (على الأقل بالنسبة الأنواع المعرفة من قبل المستخدم كما ج لا فرض أي)

  5. إشارة العد (أو شيء لتنفيذ RAII)

  6. دعم محدود معالجة الاستثناء (setjmp و longjmp)

على أعلى من المذكورة أعلاه ينبغي أن تعتمد على ANSI/ISO المواصفات و يجب أن لا تعتمد على مترجم-وظائف محددة.

انظر http://ldeniau.web.cern.ch/ldeniau/html/oopc/oopc.html.إذا كان أي شيء آخر من خلال قراءة الوثائق هو تجربة مفيدة.

أنا قليلا في وقت متأخر إلى الحزب هنا ولكن أود أن تجنب سواء ماكرو التطرف - كثيرة جدا أو الكثير من تشوش رمز ، ولكن بضع واضحة وحدات الماكرو يمكن أن تجعل OOP البرمجية أسهل لتطوير قراءة:

/*
 * 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
}

أعتقد أن هذا لديه توازن جيد, و الأخطاء يولد (على الأقل مع الافتراضي دول مجلس التعاون الخليجي 6.3 الخيارات) لبعض من أكثر احتمالا الأخطاء هي مفيدة بدلا من مربكة.بيت القصيد هو تحسين الإنتاجية مبرمج لا ؟

أنا أعمل أيضا على هذا استنادا إلى ماكرو الحل.لذلك أشجع فقط, أعتقد ;-) ولكن هو لطيف جدا بالفعل و أنا أعمل على عدة مشاريع على أعلى من ذلك.يعمل بحيث عليك أولا تحديد رأس منفصل لكل فئة.مثل هذا:

#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))               \

لتنفيذ الدرس إنشاء ملف رأس و C الملف حيث قمت بتطبيق أساليب:

METHOD(Point,public,void,draw)
{
    printf("point at %d,%d\n", self->x, self->y);
}

في رأس قمت بإنشائه لهذه الفئة تشمل رؤوس أخرى تحتاج وتحديد أنواع إلخ.ذات الصلة إلى الفئة.في كل فئة رأس في ملف C تشمل فئة مواصفات الملف (انظر أول مدونة سبيل المثال) و X-الماكرو.هذه X-وحدات الماكرو (1,2,3 .... الخ) سوف توسيع رمز إلى الدرجة الفعلية البنيات وغيرها من الإعلانات.

ترث الطبقة ، #define SUPER supername و إضافة supername__define \ كما في السطر الأول في تعريف الفئة.على حد سواء يجب أن تكون هناك.هناك أيضا دعم JSON, إشارات, فئات مجردة ، إلخ.

لإنشاء كائن فقط استخدام W_NEW(classname, .x=1, .y=2,...).التهيئة تقوم على تهيئة البنية قدم في C11.أنه يعمل بشكل جيد و كل شيء غير مدرجة يتم تعيين إلى الصفر.

استدعاء أسلوب استخدام W_CALL(o,method)(1,2,3).يبدو أعلى من أجل استدعاء دالة وإنما هو مجرد ماكرو.أنه يوسع ((o)->klass->method(o,1,2,3)) التي هي لطيفة حقا هاك.

انظر الوثائق و القانون نفسه.

منذ إطار احتياجات بعض رمز المتداول ، كتبت Perl (wobject) التي لا وظيفة.إذا كنت تستخدم ذلك ، يمكنك كتابة فقط

class Point
    public int move_up(int steps)
    public void draw()
    read int x
    read int y

وأنه سيتم إنشاء فئة مواصفات ملف فئة رأس ، ج الملف الذي يتضمن Point_impl.c حيث يمكنك تنفيذ الدرس.فإنه يوفر الكثير من العمل, إذا كان لديك العديد من دروس بسيطة ولكن لا يزال كل شيء في C. wobject هو بسيط جدا التعبير العادية على أساس الماسح الضوئي والتي هي سهلة التكيف مع احتياجات محددة ، أو أن تكون إعادة كتابة من الصفر.

مفتوحة المصدر Dynace المشروع يفعل ذلك بالضبط.انها في https://github.com/blakemcbride/Dynace

إذا كنت تحتاج إلى كتابة رمز قليلا جرب هذا: https://github.com/fulminati/class-framework

#include "class-framework.h"

CLASS (People) {
    int age;
};

int main()
{
    People *p = NEW (People);

    p->age = 10;

    printf("%d\n", p->age);
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top