Объектная ориентация в C

Принятый ответ Бобинса - это короткое портативное решение. Если вам нужно не только добавлять SVG, но и манипулировать им, вы можете попробовать библиотеку JavaScript «Pablo» (я ее написал). Он будет хорошо знаком с пользователями jQuery.

Пример вашего кода выглядел бы так:

$(document).ready(function(){
    Pablo("svg").append('');
});

Вы также можете создавать элементы SVG «на лету» без указания разметки:

var circle = Pablo.circle({
    cx:100,
    cy:50,
    r:40
}).appendTo('svg');

146
задан 9 revs, 4 users 44%Anthony Cuozzo 23 May 2017 в 12:26
поделиться

10 ответов

C объектная система (COS) обещание звуков (это находится все еще в альфа-версии). Это пытается сохранить минимальным доступные понятия ради простоты и гибкости: универсальное объектно-ориентированное программирование включая открытые классы, метаклассы, метаклассы свойства, дженерики, мультиметоды, делегацию, владение, исключения, контракты и закрытия. Существует , делают набросок документа (PDF), который описывает его.

Исключением в C является реализация C89 TRY-CATCH-FINALLY, найденного на других языках OO. Это идет с testsuite и некоторыми примерами.

Оба Laurent Deniau, который работает много над ООП в C.

30
ответ дан philant 23 May 2017 в 12:26
поделиться

Если бы я собирался записать ООП в C, то я, вероятно, пошел бы с псевдо - дизайн Pimpl. Вместо передающих указателей на структуры Вы заканчиваете передающие указатели на указатели на структуры. Это делает содержание непрозрачным и упрощает полиморфизм и наследование.

настоящая проблема с ООП в C - то, что происходит, когда переменные выходят из объема. Нет никаких сгенерированных компилятором деструкторов, и это может вызвать проблемы. Макросы могут возможно помочь, но это всегда будет ужасным для взгляда на.

1
ответ дан Peter Mortensen 23 May 2017 в 12:26
поделиться

Для меня объектная ориентация в C должна иметь эти функции:

  1. Инкапсуляция и сокрытие данных (может быть достигнут с помощью структур / непрозрачных указателей)

  2. Наследование и поддержка полиморфизма (единичное наследование может быть достигнуто с помощью структур - удостоверяются, что абстрактная основа не instantiable)

  3. Конструктор и функциональность деструктора (не легкий достигнуть)

  4. проверка Типа (по крайней мере, для пользовательских типов, поскольку C не осуществляет никого)

  5. Подсчет ссылок (или что-то для реализации RAII)

  6. Ограниченная поддержка обработки исключений (setjmp и longjmp)

вдобавок к вышеупомянутому, это должно полагаться на спецификации ANSI/ISO и не должно полагаться на определенную для компилятора функциональность.

0
ответ дан Peter Mortensen 23 May 2017 в 12:26
поделиться

ffmpeg (инструментарий для обработки видеоданных) записан в прямом C (и ассемблер), но использование объектно-ориентированного стиля. Это полно структур с указателями функции. Существует ряд функций фабрики, которые инициализируют структуры с соответствующими указателями "метода".

4
ответ дан Mr Fooz 23 May 2017 в 12:26
поделиться

Я когда-то работал с библиотекой C, которая была реализована способом, который показался мне довольно изящный. Они записали, в C, способ определить объекты, затем наследовались от них так, чтобы они были так же расширяемы как объект C++. Основная идея была этим:

  • Каждый объект имел свои собственные Государственные функции файла
  • , и переменные определяются в.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 файлы определили точно, что необходимо быть в состоянии сделать с каждым объектом.

31
ответ дан Peter Mortensen 23 May 2017 в 12:26
поделиться

Если Вы думаете о методах, обратился к объектам как к статическим методам, которые передают неявное' this' в функцию, она может сделать взгляды OO в C легче.

, Например:

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

становится:

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

Или что-то как этот.

6
ответ дан jjnguy 23 May 2017 в 12:26
поделиться

Я раньше делал такого рода вещь в C, прежде чем я знал, каково ООП было.

следующее является примером, который реализует буфер данных, который растет по требованию, учитывая минимальный размер, инкрементный и максимальный размер. Эта конкретная реализация была базирующимся "элементом", который должен сказать, что это было разработано для разрешения подобного списку набора любого типа C, не только буфера байта переменной длины.

идея состоит в том, что объект инстанцируют с помощью xxx_crt () и удалил использование xxx_dlt (). Каждый из "членских" методов берет конкретно введенный указатель для работы на.

я реализовал связанный список, циклический буфер и много других вещей этим способом.

я должен признаться, я никогда не уделял внимания о том, как реализовать наследование с этим подходом. Я предполагаю, что некоторое смешение предлагаемого Kieveli могло бы быть хорошим путем.

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

dtb.h

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

пз: vint был просто определением типа интервала - я использовал его, чтобы напомнить мне, что это - длина, было переменным от платформы до платформы (для портирования).

5
ответ дан Lawrence Dol 23 May 2017 в 12:26
поделиться

Рабочий стол GNOME для Linux записан в объектно-ориентированном C, и это имеет объектную модель, названную" GObject", который поддерживает свойства, наследование, полиморфизм, а также некоторых других положительных героев как ссылки, обработка событий (названный "сигналами"), ввод времени выполнения, частные данные, и т.д.

Это включает взломы препроцессора, чтобы сделать вещи как преобразование типа вокруг в иерархии классов и т.д. Вот класс в качестве примера, который я записал для GNOME (вещами как gchar являются определения типов):

Источник Класса

Заголовок Класса

Внутренняя часть структура GObject там является целым числом GType, которое используется в качестве магического числа для системы динамического контроля типов GLIB (можно бросить всю структуру к "GType", чтобы найти, что это - тип).

16
ответ дан James Cape 23 May 2017 в 12:26
поделиться

Я отговорил бы от препроцессора (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, иначе Вы будете случайно влиять на все объекты данного типа.

176
ответ дан haccks 23 May 2017 в 12:26
поделиться

Немного не по теме, но исходный компилятор C ++, Cfront , скомпилировал C ++ в C, а затем в ассемблер.

Сохранено здесь .

6
ответ дан 23 November 2019 в 22:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: