Czy można pisać kod obiektowy w C? [zamknięte]

Czy można pisać kod obiektowy w C? Szczególnie w odniesieniu do polimorfizmu.


Zobacz też pytanie o przepełnienie stosuorientacja obiektu w C.

 429
Author: Peter Mortensen, 2008-12-09

30 answers

Tak. W rzeczywistości Axel Schreiner dostarcza swoją książkę "Programowanie obiektowe w ANSI-C" za darmo, która obejmuje ten temat dość dokładnie.

 319
Author: mepcotterell,
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:52:13

Skoro mówisz o polimorfizmie to tak, możesz, robiliśmy takie rzeczy lata przed powstaniem C++.

Zasadniczo używasz struct do przechowywania zarówno danych, jak i listy wskaźników funkcji, aby wskazać odpowiednie funkcje dla tych danych.

Więc, w klasie komunikacyjnej, masz otwarte, Odczyt, Zapis i zamknięcie wywołania, które byłyby utrzymywane jako cztery wskaźniki funkcji w strukturze, obok danych dla obiektu, coś like:

typedef struct {
    int (*open)(void *self, char *fspec);
    int (*close)(void *self);
    int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
    int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
    // And data goes here.
} tCommClass;

tCommClass commRs232;
commRs232.open = &rs232Open;
: :
commRs232.write = &rs232Write;

tCommClass commTcp;
commTcp.open = &tcpOpen;
: :
commTcp.write = &tcpWrite;

Oczywiście powyższe segmenty kodu faktycznie byłyby w "konstruktorze" takim jak rs232Init().

Kiedy 'dziedziczysz' z tej klasy, po prostu zmieniasz wskaźniki, aby wskazywały na twoje własne funkcje. Każdy, kto wywołał te funkcje, zrobiłby to za pomocą wskaźników funkcji, dając Twój polimorfizm:

int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");

Coś w rodzaju ręcznego vtable.

Możesz mieć nawet wirtualne klasy ustawiając wskaźniki NA NULL -zachowanie byłoby nieco różni się od C++ (zrzut pamięci w czasie wykonywania, a nie błąd w czasie kompilacji).

Oto przykładowy kod, który to demonstruje. Pierwsza struktura klasy najwyższego poziomu:

#include <stdio.h>

// The top-level class.

typedef struct sCommClass {
    int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;

Wtedy mamy funkcje dla "podklasy" TCP:

// Function for the TCP 'class'.

static int tcpOpen (tCommClass *tcp, char *fspec) {
    printf ("Opening TCP: %s\n", fspec);
    return 0;
}
static int tcpInit (tCommClass *tcp) {
    tcp->open = &tcpOpen;
    return 0;
}

Oraz http:

// Function for the HTTP 'class'.

static int httpOpen (tCommClass *http, char *fspec) {
    printf ("Opening HTTP: %s\n", fspec);
    return 0;
}
static int httpInit (tCommClass *http) {
    http->open = &httpOpen;
    return 0;
}

I na koniec testowy program do pokazania go w akcji:

// Test program.

int main (void) {
    int status;
    tCommClass commTcp, commHttp;

    // Same 'base' class but initialised to different sub-classes.

    tcpInit (&commTcp);
    httpInit (&commHttp);

    // Called in exactly the same manner.

    status = (commTcp.open)(&commTcp, "bigiron.box.com:5000");
    status = (commHttp.open)(&commHttp, "http://www.microsoft.com");

    return 0;
}

To daje wyjście:

Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com

Więc widać, że różne funkcje są wywoływane, w zależności od podklasy.

 297
Author: paxdiablo,
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-12 00:39:22

Przestrzenie nazw są często wykonywane przez wykonanie:

stack_push(thing *)

Zamiast

stack::push(thing *)

Aby utworzyć C strukturę w coś w rodzaju C++ klasy można przekształcić:

class stack {
     public:
        stack();
        void push(thing *);
        thing * pop();
        static int this_is_here_as_an_example_only;
     private:
        ...
};

Do

struct stack {
     struct stack_type * my_type;
     // Put the stuff that you put after private: here
};
struct stack_type {
     void (* construct)(struct stack * this); // This takes uninitialized memory
     struct stack * (* operator_new)(); // This allocates a new struct, passes it to construct, and then returns it
     void (*push)(struct stack * this, thing * t); // Pushing t onto this stack
     thing * (*pop)(struct stack * this); // Pops the top thing off the stack and returns it
     int this_is_here_as_an_example_only;
}Stack = {
    .construct = stack_construct,
    .operator_new = stack_operator_new,
    .push = stack_push,
    .pop = stack_pop
};
// All of these functions are assumed to be defined somewhere else

I do:

struct stack * st = Stack.operator_new(); // Make a new stack
if (!st) {
   // Do something about it
} else {
   // You can use the stack
   stack_push(st, thing0); // This is a non-virtual call
   Stack.push(st, thing1); // This is like casting *st to a Stack (which it already is) and doing the push
   st->my_type.push(st, thing2); // This is a virtual call
}

Nie zrobiłem destruktora ani kasowania, ale to działa zgodnie z tym samym wzorem.

This_is_here_as_an_example_only jest jak statyczna zmienna klasy -- współdzielona pomiędzy wszystkimi instancjami danego typu. Wszystkie metody są naprawdę statyczne, z wyjątkiem tego, że niektóre biorą to *

 78
Author: nategoose,
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 16:14:13

Uważam, że implementacja OOP w C jest nie tylko użyteczna, ale także doskonałym sposobem na nauczenie się OOP i zrozumienie jego wewnętrznego działania. Doświadczenie wielu programistów pokazało, że aby efektywnie i pewnie wykorzystać technikę, programista musi zrozumieć, w jaki sposób podstawowe pojęcia są ostatecznie realizowane. Emulowanie klas, dziedziczenie i polimorfizm w C uczy właśnie tego.

Aby odpowiedzieć na oryginalne pytanie, oto kilka zasobów, które uczą jak zrobić OOP w C:

EmbeddedGurus.com wpis na blogu "Programowanie obiektowe w C" pokazuje jak zaimplementować klasy i pojedyncze dziedziczenie w przenośnym C: http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c/

Application Note "" C+" - Object Oriented Programming in C " pokazuje jak zaimplementować klasy, pojedyncze dziedziczenie i późne wiązanie (polimorfizm) w C za pomocą preprocesora makra: http://www.state-machine.com/resources/cplus_3.0_manual.pdf , przykładowy kod jest dostępny od http://www.state-machine.com/resources/cplus_3.0.zip

 49
Author: Miro,
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
2010-04-29 01:37:14

Widziałem to. Nie polecam. C++ początkowo zaczynał w ten sposób jako preprocesor, który wytwarzał kod C jako etap pośredni.

Zasadniczo to, co robisz, to tworzenie tabeli wysyłkowej dla wszystkich metod, w której przechowujesz odwołania do funkcji. Wyprowadzenie klasy pociągałoby za sobą skopiowanie tej tabeli dyspozytorskiej i zastąpienie wpisów, które chcesz nadpisać, a twoje nowe "metody" muszą wywołać oryginalną metodę, jeśli chce ona wywołać metodę bazową. W końcu piszesz c++na nowo.

 30
Author: tvanfosson,
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
2008-12-09 04:04:02

Jasne, że to możliwe. To jest to, co GObject , framework, na którym oparte są wszystkie GTK+ i GNOME.

 25
Author: Peter Mortensen,
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:57:01

Biblioteka plików C stdio jest doskonałym przykładem tworzenia abstrakcji, enkapsulacji i modułowości w nieskomplikowanym C.

Dziedziczenie i polimorfizm-inne aspekty często uważane za istotne dla OOP - niekoniecznie zapewniają obiecujący przyrost wydajności i rozsądne pojawiły się argumenty , które mogą faktycznie utrudniać rozwój i myślenie o problemie.

 17
Author: msw,
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-10-24 11:58:49

Trywialny przykład ze zwierzęciem i psem: lustro mechanizmu vtable C++(w dużej mierze i tak). Oddzielasz również alokację i instancję (Animal_Alloc, Animal_New), więc nie wywołujemy malloc () wiele razy. Musimy również jawnie przekazać wskaźnik this.

Jeśli miałbyś wykonywać funkcje nie-wirtualne, to jest to trival. Po prostu nie dodajesz ich do vtable i funkcje statyczne nie wymagają wskaźnika this. Dziedziczenie wielokrotne zazwyczaj wymaga wielu vtables do rozwiązania niejasności.

Również powinieneś być w stanie użyć setjmp/longjmp do obsługi wyjątków.

struct Animal_Vtable{
    typedef void (*Walk_Fun)(struct Animal *a_This);
    typedef struct Animal * (*Dtor_Fun)(struct Animal *a_This);

    Walk_Fun Walk;
    Dtor_Fun Dtor;
};

struct Animal{
    Animal_Vtable vtable;

    char *Name;
};

struct Dog{
    Animal_Vtable vtable;

    char *Name; // Mirror member variables for easy access
    char *Type;
};

void Animal_Walk(struct Animal *a_This){
    printf("Animal (%s) walking\n", a_This->Name);
}

struct Animal* Animal_Dtor(struct Animal *a_This){
    printf("animal::dtor\n");
    return a_This;
}

Animal *Animal_Alloc(){
    return (Animal*)malloc(sizeof(Animal));
}

Animal *Animal_New(Animal *a_Animal){
    a_Animal->vtable.Walk = Animal_Walk;
    a_Animal->vtable.Dtor = Animal_Dtor;
    a_Animal->Name = "Anonymous";
    return a_Animal;
}

void Animal_Free(Animal *a_This){
    a_This->vtable.Dtor(a_This);

    free(a_This);
}

void Dog_Walk(struct Dog *a_This){
    printf("Dog walking %s (%s)\n", a_This->Type, a_This->Name);
}

Dog* Dog_Dtor(struct Dog *a_This){
    // Explicit call to parent destructor
    Animal_Dtor((Animal*)a_This);

    printf("dog::dtor\n");

    return a_This;
}

Dog *Dog_Alloc(){
    return (Dog*)malloc(sizeof(Dog));
}

Dog *Dog_New(Dog *a_Dog){
    // Explict call to parent constructor
    Animal_New((Animal*)a_Dog);

    a_Dog->Type = "Dog type";
    a_Dog->vtable.Walk = (Animal_Vtable::Walk_Fun) Dog_Walk;
    a_Dog->vtable.Dtor = (Animal_Vtable::Dtor_Fun) Dog_Dtor;

    return a_Dog;
}

int main(int argc, char **argv){
    /*
      Base class:

        Animal *a_Animal = Animal_New(Animal_Alloc());
    */
    Animal *a_Animal = (Animal*)Dog_New(Dog_Alloc());

    a_Animal->vtable.Walk(a_Animal);

    Animal_Free(a_Animal);
}

PS. Jest to testowane na kompilatorze C++, ale powinno być łatwe, aby działało na kompilatorze C.

 15
Author: Jasper Bekkers,
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 16:02:51

Sprawdź GObject . Ma być OO W C i jedna implementacja tego czego szukasz. Jeśli naprawdę chcesz OO, przejdź do C++ lub innego języka OOP. GObject może być naprawdę trudne do pracy z czasami, jeśli jesteś przyzwyczajony do czynienia z językami OO, ale jak wszystko, przyzwyczaisz się do konwencji i przepływu.

 12
Author: NG.,
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
2010-04-28 19:53:40

To było ciekawe do przeczytania. Sam zastanawiałem się nad tym samym pytaniem, a korzyści płynące z myślenia o nim są takie:

  • Próba wyobrażenia sobie, jak zaimplementować pojęcia OOP w języku Nie-OOP pomaga mi zrozumieć mocne strony języka OOp (w moim przypadku C++). To pomaga mi lepiej ocenić, czy używać C lub c++ dla danego typu aplikacji-gdzie korzyści z jednego out-waży drugi.

  • W moim przeglądaniu stron dla informacje i opinie na ten temat znalazłem autora, który pisał kod dla wbudowanego procesora i miał dostępny tylko kompilator C: http://www.eetimes.com/discussion/other/4024626/Object-Oriented-C-Creating-Foundation-Classes-Part-1

W jego przypadku analiza i adaptacja koncepcji OOP w prostym C była słusznym dążeniem. Wydaje się, że był otwarty na rezygnację z niektórych koncepcji OOP ze względu na hit wydajności wynikający z próby ich wdrożenia w C.

Lekcja, którą wziąłem, jest taka, że można to zrobić do pewnego stopnia, i tak, jest kilka dobrych powodów, aby spróbować.

W końcu maszyna krąży po bitach wskaźnika stosu, powodując, że licznik programu przeskakuje i oblicza operacje dostępu do pamięci. Z punktu widzenia wydajności, im mniej tych obliczeń wykonanych przez twój program, tym lepiej... ale czasami musimy zapłacić ten podatek po prostu, abyśmy mogli zorganizować nasz program w sposób, który czyni go najmniej podatnym za ludzki błąd. Kompilator języka OOP dąży do optymalizacji obu aspektów. Programista musi być znacznie ostrożniejszy w implementacji tych pojęć w języku takim jak C.

 12
Author: RJB,
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 16:28:40

Pomocne może okazać się przejrzenie dokumentacji Apple dla podstawowego zestawu interfejsów API. Jest to czyste C API, ale wiele typów jest łączonych z obiektowymi odpowiednikami Objective-C.

Pomocne może okazać się również przyjrzenie się samej konstrukcji Objective-C. Jest nieco inny od C++ tym, że system obiektowy jest zdefiniowany w kategoriach funkcji C, np. objc_msg_send do wywołania metody na obiekcie. Kompilator tłumaczy składnię nawiasów kwadratowych na te wywołania funkcji, więc nie musisz to wiedzieć, ale biorąc pod uwagę twoje pytanie, może okazać się przydatne, aby dowiedzieć się, jak to działa pod maską.

 10
Author: benzado,
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
2010-04-28 19:58:36

Istnieje kilka technik, które można wykorzystać. Najważniejsze jest to, jak podzielić projekt. Używamy interfejsu w naszym projekcie, który jest zadeklarowany w .plik h i implementacja obiektu w a .plik C. Ważną częścią jest to, że wszystkie moduły, które zawierają .plik H widzi tylko obiekt jako void *, aplik c jest jedynym modułem, który zna wewnętrzne elementy struktury.

Coś takiego dla klasy, którą nazywamy FOO jako przykład:

W .h plik

#ifndef FOO_H_
#define FOO_H_

...
 typedef struct FOO_type FOO_type;     /* That's all the rest of the program knows about FOO */

/* Declaration of accessors, functions */
FOO_type *FOO_new(void);
void FOO_free(FOO_type *this);
...
void FOO_dosomething(FOO_type *this, param ...):
char *FOO_getName(FOO_type *this, etc);
#endif

Plik implementacji C będzie czymś takim.

#include <stdlib.h>
...
#include "FOO.h"

struct FOO_type {
    whatever...
};


FOO_type *FOO_new(void)
{
    FOO_type *this = calloc(1, sizeof (FOO_type));

    ...
    FOO_dosomething(this, );
    return this;
}

Więc daję wskaźnik jawnie obiektowi każdej funkcji tego modułu. Kompilator C++ robi to niejawnie, a w C zapisujemy to wprost.

Naprawdę używam this w moich programach, aby upewnić się, że mój program nie kompiluje się w C++, a jego właściwość polega na tym, że jest w innym kolorze w moim edytorze podświetlania składni.

Pola FOO_struct można modyfikować w jeden moduł i drugi moduł nawet nie muszą być rekompilowane, aby być nadal użyteczne.

W tym stylu zajmuję się już dużą częścią zalet OOP (Data encapsulation). Używając wskaźników funkcji, można nawet łatwo zaimplementować coś w rodzaju dziedziczenia, ale szczerze mówiąc, jest to naprawdę rzadko przydatne.

 10
Author: Patrick Schlüter,
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 16:12:55

Object oriented C, da się zrobić, widziałem ten typ kodu w produkcji w Korei i był to najstraszniejszy Potwór, jakiego widziałem od lat (to było jak w zeszłym roku (2007), że widziałem kod). Tak więc tak można to zrobić, i tak ludzie robili to wcześniej i nadal robią to nawet w dzisiejszych czasach. Ale polecam C++ lub Objective-C, oba są językami zrodzonymi z C, w celu zapewnienia orientacji obiektowej z różnymi paradygmatami.

 7
Author: Robert Gould,
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
2008-12-09 04:09:17

Jeśli jesteś przekonany, że podejście OOP jest lepsze dla problemu, który próbujesz rozwiązać, dlaczego starasz się rozwiązać go za pomocą języka Nie-OOP? Wygląda na to, że używasz niewłaściwego narzędzia. Użyj C++ lub innego obiektowo zorientowanego języka C.

Jeśli pytasz, ponieważ zaczynasz kodować na już istniejącym dużym projekcie napisanym w C, nie powinieneś próbować wprowadzać własnych (lub cudzych) paradygmatów OOP do infrastruktury projektu. Postępuj zgodnie z wytycznymi, które są już obecne w projekcie. Ogólnie rzecz biorąc, czyste interfejsy API oraz izolowane biblioteki i moduły pójdą długą drogę w kierunku czystego projektu OOP-ish.

Jeśli po tym wszystkim naprawdę jesteś nastawiony na robienie OOP C, przeczytaj to (PDF).

 7
Author: RarrRarrRarr,
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
2010-04-28 19:49:02

Można to sfałszować za pomocą wskaźników funkcyjnych, a tak naprawdę myślę, że teoretycznie możliwe jest skompilowanie programów C++ do C.

Rzadko jednak ma sens wymuszanie paradygmatu na języku, a nie Wybieranie języka, który używa paradygmatu.

 6
Author: Uri,
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
2008-12-09 04:05:56

Tak, możesz. Ludzie pisali zorientowane obiektowo C zanim pojawiły się C++ lub Objective-C. Zarówno C++ , jak i Objective - C były częściowo próbami przyjęcia niektórych pojęć OO używanych w C i sformalizowania ich jako części języka.

Oto naprawdę prosty program, który pokazuje, jak można zrobić coś, co wygląda jak/jest wywołaniem metody (są lepsze sposoby, aby to zrobić. Jest to tylko dowód, że Język Obsługuje pojęcia):

#include<stdio.h>

struct foobarbaz{
    int one;
    int two;
    int three;
    int (*exampleMethod)(int, int);
};

int addTwoNumbers(int a, int b){
    return a+b;
}

int main()
{
    // Define the function pointer
    int (*pointerToFunction)(int, int) = addTwoNumbers;

    // Let's make sure we can call the pointer
    int test = (*pointerToFunction)(12,12);
    printf ("test: %u \n",  test);

    // Now, define an instance of our struct
    // and add some default values.
    struct foobarbaz fbb;
    fbb.one   = 1;
    fbb.two   = 2;
    fbb.three = 3;

    // Now add a "method"
    fbb.exampleMethod = addTwoNumbers;

    // Try calling the method
    int test2 = fbb.exampleMethod(13,36);
    printf ("test2: %u \n",  test2);

    printf("\nDone\n");
    return 0;
}
 6
Author: Alan Storm,
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 16:00:07

Oczywiście nie będzie to tak piękne, jak używanie języka z wbudowaną obsługą. Napisałem nawet "obiektowy asembler".

 6
Author: Darron,
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 16:03:30

Mały kod OOC do dodania:

#include <stdio.h>

struct Node {
    int somevar;
};

void print() {
    printf("Hello from an object-oriented C method!");
};

struct Tree {
    struct Node * NIL;
    void (*FPprint)(void);
    struct Node *root;
    struct Node NIL_t;
} TreeA = {&TreeA.NIL_t,print};

int main()
{
    struct Tree TreeB;
    TreeB = TreeA;
    TreeB.FPprint();
    return 0;
}
 6
Author: 2 revs, 2 users 77%user922475,
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 16:29:51

Kopałem to od roku:

Ponieważ system GObject jest trudny w użyciu z czystym C, próbowałem napisać kilka ładnych makr, aby ułatwić styl OO z C.

#include "OOStd.h"

CLASS(Animal) {
    char *name;
    STATIC(Animal);
    vFn talk;
};
static int Animal_load(Animal *THIS,void *name) {
    THIS->name = name;
    return 0;
}
ASM(Animal, Animal_load, NULL, NULL, NULL)

CLASS_EX(Cat,Animal) {
    STATIC_EX(Cat, Animal);
};
static void Meow(Animal *THIS){
    printf("Meow!My name is %s!\n", THIS->name);
}

static int Cat_loadSt(StAnimal *THIS, void *PARAM){
    THIS->talk = (void *)Meow;
    return 0;
}
ASM_EX(Cat,Animal, NULL, NULL, Cat_loadSt, NULL)


CLASS_EX(Dog,Animal){
    STATIC_EX(Dog, Animal);
};

static void Woof(Animal *THIS){
    printf("Woof!My name is %s!\n", THIS->name);
}

static int Dog_loadSt(StAnimal *THIS, void *PARAM) {
    THIS->talk = (void *)Woof;
    return 0;
}
ASM_EX(Dog, Animal, NULL, NULL, Dog_loadSt, NULL)

int main(){
    Animal *animals[4000];
    StAnimal *f;
    int i = 0;
    for (i=0; i<4000; i++)
    {
        if(i%2==0)
            animals[i] = NEW(Dog,"Jack");
        else
            animals[i] = NEW(Cat,"Lily");
    };
    f = ST(animals[0]);
    for(i=0; i<4000; ++i) {
        f->talk(animals[i]);
    }
    for (i=0; i<4000; ++i) {
        DELETE0(animals[i]);
    }
    return 0;
}

Oto moja strona projektu (nie mam czasu na pisanie en. doc, jednak doc po chińsku jest znacznie lepszy).

OOC-GCC

 5
Author: dameng,
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 16:34:04

Jest przykład dziedziczenia przy użyciu C w przemówieniu Jima Larsona z 1996 roku wygłoszonym na Sekcja 312 Programming Lunchtime Seminar Tutaj: wysoki i niski poziom C .

 4
Author: Judge Maygarden,
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
2008-12-09 04:50:32

Które artykuły lub książki są dobre, aby używać pojęć OOP w C?

Dave Hanson ' s C Interfaces and Implementations is excellent on enkapsulation and naming and very good on use of function pointers. Dave nie próbuje symulować dziedziczenia.

 4
Author: Norman Ramsey,
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
2010-04-29 02:38:33

OOP jest tylko paradygmatem, w którym dane są ważniejsze niż kod w programach. OOP nie jest językiem. Tak jak zwykły C jest prostym językiem, OOP w prostym C też jest prosty.

 4
Author: anonyme,
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-01-15 23:50:59

Jedną z rzeczy, które powinieneś zrobić, to przyjrzeć się implementacjiXT zestawu narzędzi dlaX Window . Oczywiście, że jest to coraz długo w zębie, ale wiele z konstrukcji używanych zostały zaprojektowane do pracy w sposób oo w tradycyjnym C. Ogólnie oznacza to dodanie dodatkowej warstwy indrection tu i tam i projektowanie struktur, aby leżały na siebie.

Można naprawdę wiele zrobić w sposób oo położony w C w ten sposób, mimo że czasami tak się wydaje, OO pojęcia nie powstały w pełni z umysłu #include<favorite_OO_Guru.h>. W rzeczywistości stanowiły one wiele z ustalonych najlepszych praktyk tamtych czasów. OO Języki i systemy tylko wydestylowane i wzmocnione części programu zeitgeist dnia.

 4
Author: Peter Mortensen,
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 16:26:23

Odpowiedź na pytanie brzmi "tak, możesz".

Object-oriented C (OOC) kit jest przeznaczony dla tych, którzy chcą programować w sposób obiektowy, ale trzymają się również starego, dobrego C. OOC implementuje klasy, dziedziczenie pojedyncze i wielokrotne, obsługę wyjątków.

Cechy

• używa tylko makr i funkcji C, nie wymaga rozszerzeń językowych! (ANSI-C)

* łatwy do odczytania kod źródłowy dla Twojej aplikacji. Zadbano o to, aby wszystko było tak proste jak to możliwe.

* pojedyncze dziedziczenie klas

* wielokrotne dziedziczenie przez interfejsy i mixiny (od wersji 1.3)

• Implementowanie WYJĄTKÓW (w czystym C!)

* funkcje wirtualne dla klas

* zewnętrzne narzędzie do łatwej implementacji klas

Aby uzyskać więcej informacji, odwiedź http://ooc-coding.sourceforge.net/.

 4
Author: Sachin Mhetre,
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 16:36:39

Wygląda na to, że ludzie próbują emulować styl C++ za pomocą C. moim zdaniem programowanie obiektowe C to tak naprawdę programowanie zorientowane strukturalnie. Można jednak osiągnąć takie rzeczy, jak późne wiązanie, hermetyzacja i dziedziczenie. W przypadku dziedziczenia jawnie definiujesz wskaźnik do struktur bazowych w swojej strukturze podrzędnej i jest to oczywiście forma dziedziczenia wielokrotnego. Musisz również określić, czy Twój

//private_class.h
struct private_class;
extern struct private_class * new_private_class();
extern int ret_a_value(struct private_class *, int a, int b);
extern void delete_private_class(struct private_class *);
void (*late_bind_function)(struct private_class *p);

//private_class.c
struct inherited_class_1;
struct inherited_class_2;

struct private_class {
  int a;
  int b;
  struct inherited_class_1 *p1;
  struct inherited_class_2 *p2;
};

struct inherited_class_1 * new_inherited_class_1();
struct inherited_class_2 * new_inherited_class_2();

struct private_class * new_private_class() {
  struct private_class *p;
  p = (struct private_class*) malloc(sizeof(struct private_class));
  p->a = 0;
  p->b = 0;
  p->p1 = new_inherited_class_1();
  p->p2 = new_inherited_class_2();
  return p;
}

    int ret_a_value(struct private_class *p, int a, int b) {
      return p->a + p->b + a + b;
    }

    void delete_private_class(struct private_class *p) {
      //release any resources
      //call delete methods for inherited classes
      free(p);
    }
    //main.c
    struct private_class *p;
    p = new_private_class();
    late_bind_function = &implementation_function;
    delete_private_class(p);

Skompiluj z c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj.

Więc rada jest do trzymaj się czystego stylu C i nie próbuj wymuszać stylu C++. Również ten sposób nadaje się do bardzo czystego sposobu budowania API.

 4
Author: 4 revs, 2 users 91%user2074102,
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 16:38:53

Zobacz http://slkpg.byethost7.com/instance.html dla jeszcze jednej zmiany na OOP w C. podkreśla dane instancji dla reentrancy używając tylko natywnego C. wielokrotne dziedziczenie jest wykonywane ręcznie za pomocą opakowywania funkcji. Bezpieczeństwo typu jest utrzymywane. Oto mała próbka:

typedef struct _peeker
{
    log_t     *log;
    symbols_t *sym;
    scanner_t  scan;            // inherited instance
    peek_t     pk;
    int        trace;

    void    (*push) ( SELF *d, symbol_t *symbol );
    short   (*peek) ( SELF *d, int level );
    short   (*get)  ( SELF *d );
    int     (*get_line_number) ( SELF *d );

} peeker_t, SlkToken;

#define push(self,a)            (*self).push(self, a)
#define peek(self,a)            (*self).peek(self, a)
#define get(self)               (*self).get(self)
#define get_line_number(self)   (*self).get_line_number(self)

INSTANCE_METHOD
int
(get_line_number) ( peeker_t *d )
{
    return  d->scan.line_number;
}

PUBLIC
void
InitializePeeker ( peeker_t  *peeker,
                   int        trace,
                   symbols_t *symbols,
                   log_t     *log,
                   list_t    *list )
{
    InitializeScanner ( &peeker->scan, trace, symbols, log, list );
    peeker->log = log;
    peeker->sym = symbols;
    peeker->pk.current = peeker->pk.buffer;
    peeker->pk.count = 0;
    peeker->trace = trace;

    peeker->get_line_number = get_line_number;
    peeker->push = push;
    peeker->get = get;
    peeker->peek = peek;
}
 2
Author: slkpg,
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-28 13:08:11

Jestem trochę spóźniony na imprezę, ale chcę podzielić się moimi doświadczeniami na ten temat: pracuję obecnie z embedded stuff, a jedynym (niezawodnym) kompilatorem, jaki mam, jest C, więc chcę zastosować podejście obiektowe w moich projektach embedded napisanych w C.

WiÄ ™ kszoĹ "Ä ‡ rozwiÄ ... zaĹ" jakie do tej pory widziaĹ 'em uĺźywaä ‡ typecastăłw, wiÄ ™ c tracimy bezpieczeĹ" stwo typu: kompilator Ci nie pomoĹźe, jeĹ " li pomylisz siÄ™. Jest to całkowicie niedopuszczalne.

Wymagania jakie mam:

  • unikaj typecasts jak najbardziej, więc nie tracimy bezpieczeństwa typu;
  • polimorfizm: powinniśmy być w stanie używać metod wirtualnych, a użytkownik klasy nie powinien być świadomy, czy jakaś konkretna metoda jest wirtualna, czy nie;
  • wielokrotne dziedziczenie: nie używam go często, ale czasami naprawdę chcę, aby jakaś Klasa zaimplementowała wiele interfejsów(lub rozszerzyła wiele superklas).

Swoje podejście wyjaśniłem szczegółowo w tym artykule: Programowanie obiektowe w C ; dodatkowo, istnieje narzędzie do autogeneracji kodu kotła dla klas bazowych i pochodnych.

 2
Author: Dmitry Frank,
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-03-19 13:57:45

Zbudowałem małą bibliotekę, gdzie próbowałem tego i dla mnie działa naprawdę ładnie. Więc pomyślałem, że podzielę się tym doświadczeniem.

Https://github.com/thomasfuhringer/oxygen

Pojedyncze dziedziczenie może być zaimplementowane dość łatwo, używając struktury i rozszerzając ją dla każdej innej klasy potomnej. Prosty rzut do struktury rodzica umożliwia użycie metod rodzica na wszystkich potomkach. Dopóki wiesz, że zmienna wskazuje na strukturę trzymającą ten rodzaj obiektu, możesz zawsze może rzucić do klasy root i zrobić introspekcję.

Jak już wspomniano, metody wirtualne są nieco trudniejsze. Ale są wykonalne. Aby wszystko było proste, po prostu używam tablicy funkcji w strukturze opisu klasy, które każda klasa potomna kopiuje i przepopuluje poszczególne sloty tam, gdzie jest to wymagane.

Dziedziczenie wielokrotne byłoby raczej skomplikowane w implementacji i ma znaczący wpływ na wydajność. Więc to zostawiam. Uważam, że jest to pożądane i przydatne w sporo przypadków, aby czysto modelować rzeczywiste okoliczności życiowe, ale prawdopodobnie w 90% przypadków pojedyncze dziedziczenie pokrywa potrzeby. A pojedyncze dziedziczenie jest proste i nic nie kosztuje.

Również nie dbam o bezpieczeństwo typu. Myślę, że nie powinieneś polegać na kompilatorze, aby zapobiec błędom w programowaniu. I chroni cię tylko przed raczej małą częścią błędów i tak.

Zazwyczaj w środowisku zorientowanym obiektowo chcesz również zaimplementować liczenie referencji w celu automatyzacji zarządzanie pamięcią w miarę możliwości. Tak więc dodałem również liczbę referencji do klasy" Object " root i pewną funkcjonalność do hermetyzacji alokacji i dealokacji pamięci sterty.

To wszystko jest bardzo proste i chude i daje mi podstawowe oo bez zmuszania mnie do czynienia z potworem, jakim jest C++. A ja zachowuję elastyczność pobytu w Krainie C, co między innymi ułatwia integrację bibliotek stron trzecich.

 2
Author: Thomas F.,
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-03-02 08:38:39

Proponuję użyć Objective-C, czyli supersetu C.

Objective-C ma 30 lat, ale pozwala na pisanie eleganckiego kodu.

Http://en.wikipedia.org/wiki/Objective-C

 1
Author: SteAp,
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
2010-08-27 22:42:13

Tak, ale nigdy nie widziałem, żeby ktoś próbował zaimplementować jakikolwiek polimorfizm z C.

 0
Author: Paul Morel,
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
2008-12-09 04:06:03