Orientacja obiektu w C

Jaki byłby zestaw sprytnych hacków preprocesora (zgodnych z ANSI C89/ISO C90), które umożliwiają jakąś brzydką (ale użyteczną) orientację obiektów w C?

Znam kilka różnych języków obiektowych, więc proszę nie odpowiadać na pytania typu " Learn C++!". Przeczytałem "Programowanie obiektowe z ANSI C "(Uwaga: Format PDF) i kilka innych ciekawych rozwiązań, ale przede wszystkim interesują mnie Twoje: -)!


Zobacz Czy można pisać kod obiektowy w C?

 133

18 answers

C Object System (COS) brzmi obiecująco (nadal jest w wersji alpha). Stara się zachować jak najmniejsze dostępne koncepcje ze względu na prostotę i elastyczność: jednolite programowanie obiektowe obejmujące klasy otwarte, metaklasy, metaklasy własności, generyki, multimetody, delegacje, własność, wyjątki, umowy i zamknięcia. Istnieje projekt dokumentu (PDF), który go opisuje.

Wyjątek w C jest implementacją C89 TRY-CATCH-FINALLY found in other oo languages. Pochodzi z testsuite i kilka przykładów.

Oba autorstwa Laurenta Deniau, który dużo pracuje nad OOP w C .

 26
Author: philant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-15 18:56:36

Odradzam używanie preprocesora (ab) w celu uczynienia składni C bardziej podobną do składni innego języka zorientowanego obiektowo. Na najbardziej podstawowym poziomie, po prostu używasz zwykłych struktur jako obiektów i przekazujesz je za pomocą wskaźników:

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

void monkey_dance(struct monkey *monkey)
{
    /* do a little dance */
}

Aby uzyskać rzeczy takie jak dziedziczenie i polimorfizm, musisz pracować trochę ciężej. Dziedziczenie ręczne można wykonać, gdy pierwszy członek struktury jest instancją klasy nadrzędnej, a następnie można rzucać wokół wskaźników do bazy i pochodnej klasy dowolnie:

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

Aby uzyskać polimorfizm (np. funkcje wirtualne), używasz wskaźników funkcji i opcjonalnie tabel wskaźników funkcji, znanych również jako tablice wirtualne lub 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;
}
I tak się robi polimorfizm w C. nie jest ładny, ale robi swoje. Istnieją pewne lepkie problemy związane z rzutami wskaźników między klasami bazowymi i pochodnymi, które są bezpieczne, dopóki klasa bazowa jest pierwszym członkiem klasy pochodnej. Dziedziczenie wielokrotne jest znacznie trudniejsze - w w tym przypadku, w celu przypadku między klasami bazowymi innymi niż pierwsza, musisz ręcznie dostosować wskaźniki w oparciu o odpowiednie przesunięcia, co jest naprawdę trudne i podatne na błędy.

Kolejną (trudną) rzeczą, którą możesz zrobić, jest zmiana dynamicznego typu obiektu w czasie wykonywania! Po prostu przypisujesz mu nowy wskaźnik vtable. Możesz nawet selektywnie zmieniać niektóre funkcje wirtualne, zachowując inne, tworząc nowe typy hybrydowe. Tylko uważaj, aby utworzyć nową vtable zamiast modyfikować global vtable, w przeciwnym razie przypadkowo wpłynie na wszystkie obiekty danego typu.

 163
Author: Adam Rosenfield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-27 21:21:58

Kiedyś pracowałem z biblioteką C, która została zaimplementowana w sposób, który wydawał mi się dość elegancki. Napisali w języku C sposób definiowania obiektów, a następnie dziedziczyli po nich tak, że były tak rozszerzalne jak obiekt C++. Podstawowa idea była taka:

  • każdy obiekt miał swój własny plik
  • publiczne funkcje i zmienne są zdefiniowane w .plik h dla obiektu
  • prywatne zmienne i funkcje znajdowały się tylko wplik c
  • aby "dziedziczyć" tworzona jest nowa struktura pierwszy element struktury jest obiektem do dziedziczenia z

Dziedziczenie jest trudne do opisania, ale w zasadzie było tak:

struct vehicle {
   int power;
   int weight;
}

Następnie w innym pliku:

struct van {
   struct vehicle base;
   int cubic_size;
}

Wtedy można mieć Vana stworzonego w pamięci i używanego przez kod, który wiedział tylko o pojazdach:

struct van my_van;
struct vehicle *something = &my_van;
vehicle_function( something );
[3]}to działało pięknie, i .pliki h definiowały dokładnie, co powinno być w stanie zrobić z każdym obiektem.
 29
Author: Kieveli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-31 15:39:36

Pulpit GNOME dla Linuksa jest napisany w zorientowanym obiektowo C i posiada model obiektowy o nazwie" GObject ", który obsługuje właściwości, dziedziczenie, polimorfizm, a także niektóre inne gadżety, takie jak referencje, obsługa zdarzeń (zwane "sygnałami"), typowanie w trybie runtime, Prywatne Dane itp.

Zawiera hacki preprocesora do robienia takich rzeczy jak typecasting w hierarchii klas, itp. Oto przykładowa klasa, którą napisałem dla GNOME (rzeczy takie jak gchar są typedefs):

Klasa Źródło

Class Header

Wewnątrz struktury GObject znajduje się gtype integer, która jest używana jako magiczna liczba dla dynamicznego systemu typowania GLib (możesz przenieść całą strukturę do "GType", aby znaleźć jej typ).

 16
Author: James Cape,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-29 15:41:25

Jeśli myślisz o metodach wywoływanych na obiektach jak o metodach statycznych, które przekazują do funkcji Ukryte ' this', może to ułatwić myślenie OO W C.

Na przykład:

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

Staje się:

string s = "hi";
printf(length(s)); // pass in s, as an implicit this
Czy coś w tym stylu.
 6
Author: jjnguy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-06 05:04:55

Kiedyś robiłem takie rzeczy w C, Zanim wiedziałem, co to jest OOP.

Poniżej znajduje się przykład, który implementuje bufor danych, który rośnie na żądanie, biorąc pod uwagę minimalny rozmiar, przyrost i maksymalny rozmiar. Ta konkretna implementacja była oparta na "elemencie", co oznacza, że została zaprojektowana tak, aby umożliwić zbiór podobny do listy dowolnego typu C, a nie tylko bufor bajtów o zmiennej długości.

Idea jest taka, że obiekt jest inicjowany za pomocą xxx_crt() i usuwany za pomocą xxx_dlt(). Każdy z metody "member" przyjmują specjalnie wpisany wskaźnik do działania.

Zaimplementowałem w ten sposób listę połączoną, bufor cykliczny i wiele innych rzeczy.

Muszę przyznać, że nigdy nie myślałem o tym, jak wdrożyć dziedziczenie z takim podejściem. Wyobrażam sobie, że jakaś mieszanka oferowana przez Kieveli może być dobrą ścieżką.

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

PS: vint był po prostu typem int - użyłem go, aby przypomnieć mi, że jego długość był zmienny z platformy na platformę(do przenoszenia).

 5
Author: Lawrence Dol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-08 03:04:48

Nieco poza tematem, ale oryginalny kompilator C++, Cfront , skompilował C++ do C, a następnie do asemblera.

Zachowane tutaj .

 5
Author: zebrabox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-31 15:48:15

Ffmpeg (zestaw narzędzi do przetwarzania wideo) jest napisany w prostym języku C (i języku asemblowania), ale przy użyciu stylu obiektowego. Jest pełna struktur ze wskaźnikami funkcji. Istnieje zestaw funkcji fabrycznych, które inicjalizują struktury za pomocą odpowiednich wskaźników "method".

 4
Author: Mr Fooz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-01-06 04:31:34

Jeśli naprawdę myślisz catefully, nawet standardowa biblioteka C używa OOP-consider FILE * jako przykład: fopen() inicjalizuje obiekt FILE *, a ty go używasz, używając metod member fscanf(), fprintf(), fread(), fwrite() i innych, a ostatecznie sfinalizować je fclose().

Możesz również użyć pseudo-Objective-C, co nie jest trudne:

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;
}
// ...

Do użycia:

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

To może wynikać z takiego kodu Objective-C, Jeśli dość stary Objective-C-to-C używany jest słownik:

@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];
}
 3
Author: Maxthon Chan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-02 15:56:54

Myślę, że to, co napisał Adam Rosenfield, jest poprawnym sposobem robienia OOP w C. chciałbym dodać, że to, co pokazuje, to implementacja obiektu. Innymi słowy, rzeczywista implementacja byłaby umieszczona w pliku .c, Podczas Gdy interfejs byłby umieszczony w pliku nagłówka .h. Na przykład, używając powyższego przykładu małpy:

Interfejs wyglądałby następująco:

//monkey.h

    struct _monkey;

    typedef struct _monkey monkey;

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

Możesz zobaczyć w pliku interface .h, w którym definiujesz tylko prototypy. Następnie możesz skompilować część implementacji ".c file " do biblioteki statycznej lub dynamicznej. Tworzy to enkapsulację, a także można dowolnie zmieniać implementację. Użytkownik obiektu nie musi wiedzieć prawie nic o jego realizacji. To również kładzie nacisk na ogólny projekt obiektu.

To moje osobiste przekonanie, że oop jest sposobem konceptualizacji struktury kodu i wielokrotnego użytku i nie ma nic wspólnego z tymi innymi rzeczami, które są dodawane do c++, takimi jak przeciążenie lub szablonów. Tak, są to bardzo ładne, użyteczne funkcje, ale nie są reprezentatywne dla tego, czym tak naprawdę jest programowanie obiektowe.

 3
Author: 2 revsuser2074102,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-08 16:09:47

Moje zalecenie: trzymaj to proste. Jednym z największych problemów jest utrzymywanie starszego oprogramowania (czasami ponad 10 lat). Jeśli kod nie jest prosty, może być trudny. Tak, można pisać bardzo przydatne OOP z polimorfizmem w C, ale może to być trudne do odczytania.

Wolę proste obiekty, które zawierają pewne dobrze zdefiniowane funkcje. Doskonałym tego przykładem jest GLIB2 , na przykład tabela hash:

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

Klucze to:

  1. proste architektura i wzorce projektowe
  2. osiąga podstawową enkapsulację OOP.
  3. Łatwe do zaimplementowania, odczytania, zrozumienia i utrzymania]}
 1
Author: Wadester,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-10 19:59:19

Gdybym miał napisać OOP w C to pewnie wybrałbym pseudo - pimpl . Zamiast przekazywania wskaźników do struktur, kończy się przekazywaniem wskaźników do struktur. To sprawia, że zawartość jest nieprzezroczysta i ułatwia polimorfizm i dziedziczenie.

Prawdziwym problemem z OOP w C jest to, co dzieje się, gdy zmienne kończą zakres. Nie ma destruktorów generowanych przez kompilator, co może powodować problemy. makra mogą ewentualnie pomóc, ale zawsze będzie brzydko spójrz.

 1
Author: jmucchiello,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-31 15:47:21
#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);
}

Wyjście:

6.56
13.12

Oto pokaz czym jest programowanie OO z C.

To jest prawdziwe, czyste C, bez makr preprocesora. Mamy dziedziczenie, polimorfizm i enkapsulacja danych (w tym danych prywatnych do klas lub obiektów). Nie ma szans na ekwiwalent kwalifikatora chronionego, czyli, prywatne dane są również prywatne w łańcuchu innheritance. Ale to nie jest niedogodność, ponieważ nie sądzę, że jest to konieczne.

CPolygon nie jest utworzone, ponieważ używamy go tylko do manipulowania obiektami w dół łańcucha innheritance, które mają wspólne aspekty, ale różne ich realizacja (polimorfizm).

 0
Author: rogergc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-24 10:21:39

@Adam Rosenfield ma bardzo dobre wyjaśnienie jak osiągnąć OOP z C

Poza tym polecam przeczytać

1) pjsip

Bardzo dobra biblioteka C dla VoIP. Możesz dowiedzieć się, jak osiąga OOP poprzez struktury i tabele wskaźników funkcji

2) iOS Runtime

Dowiedz się, jak działa iOS Runtime Objective C. osiąga OOP poprzez wskaźnik isa, meta class

 0
Author: onmyway133,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-31 07:38:54

Dla mnie orientacja obiektu w C powinna mieć następujące cechy:

  1. Enkapsulacja i ukrywanie danych (można osiągnąć za pomocą struktur/nieprzezroczystych wskaźników)

  2. Dziedziczenie i wsparcie dla polimorfizmu (pojedyncze dziedziczenie można osiągnąć za pomocą struktur - upewnij się, że abstrakcyjna baza nie jest instancyjna)

  3. Funkcjonalność konstruktora i destruktora (niełatwa do osiągnięcia)

  4. Sprawdzanie typu (przynajmniej dla typów zdefiniowanych przez użytkownika jako C nie wymusza any)

  5. Zliczanie referencji (lub coś do zaimplementowania RAII)

  6. Ograniczona obsługa obsługi wyjątków (setjmp i longjmp)

Poza tym powinien opierać się na specyfikacjach ANSI/ISO i nie powinien polegać na funkcjonalności specyficznej dla kompilatora.

 0
Author: FL4SOF,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-31 15:44:16

Spójrz na http://ldeniau.web.cern.ch/ldeniau/html/oopc/oopc.html . jeśli nic innego czytanie dokumentacji nie jest pouczającym doświadczeniem.

 0
Author: Tim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-08 12:39:35

Jestem trochę spóźniony na imprezę, ale lubię unikaćobu ekstremalnych makr-za dużo lub za dużo zaciemnia kod , ale kilka oczywistych makr może ułatwić tworzenie i czytanie kodu 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
}

Myślę, że ma to dobrą równowagę, a generowane błędy (przynajmniej przy domyślnych opcjach gcc 6.3) dla niektórych bardziej prawdopodobnych błędów są pomocne, a nie mylące. Chodzi o to, aby poprawić wydajność programisty nie?

 0
Author: duanev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-06 17:01:35

Jeśli potrzebujesz napisać mały kod spróbuj tego: 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);
}
 -2
Author: cicciodarkast,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-26 00:28:35