Hoe zou een object-georiënteerde code in c schrijven?

Wat zijn enkele manieren om objectgeoriënteerde code in C te schrijven? Vooral met betrekking tot polymorfisme.


Zie ook deze stapel overloop vraag object-oriëntatie in C .


Antwoord 1, Autoriteit 100%

Ja. In feite biedt Axel Schreiner zijn boek “objectgeoriënteerde programmering in ANSI -C “gratis die het onderwerp vrij grondig bedekt.


Antwoord 2, Autoriteit 97%

Sinds je het over polymorfisme hebt, dan ja, je kunt, we deden dat soort dingen jaren voordat C++ tot stand kwam.

In principe gebruikt u een structom zowel de gegevens als een lijst met functiewijzers vast te houden om op de relevante functies voor die gegevens te wijzen.

Dus, in een communicatieklasse, zou je een open, lezen, schrijven en sluiten, die zou worden gehandhaafd als vier functiewijzers in de structuur, naast de gegevens voor een object, zoiets als:

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;

Natuurlijk zouden die codesegmenten hierboven in een “constructor” zijn, zoals rs232Init().

Als je van die klasse ‘erft’, verander je gewoon de aanwijzers om naar je eigen functies te wijzen. Iedereen die die functies aanriep, zou het doen via de functiewijzers, waardoor je je polymorfisme krijgt:

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

Zoiets als een handmatige vtable.

Je zou zelfs virtuele klassen kunnen hebben door de aanwijzers in te stellen op NULL – het gedrag zou iets anders zijn dan C++ (een kerndump tijdens runtime in plaats van een fout tijdens het compileren).

Hier is een stukje voorbeeldcode die het demonstreert. Eerst de klassenstructuur op het hoogste niveau:

#include <stdio.h>
// The top-level class.
typedef struct sCommClass {
    int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;

Dan hebben we de functies voor de TCP ‘subklasse’:

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

En ook de HTTP-versie:

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

En tot slot een testprogramma om het in actie te laten zien:

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

Dit levert de uitvoer op:

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

zodat je kunt zien dat de verschillende functies worden aangeroepen, afhankelijk van de subklasse.


Antwoord 3, autoriteit 24%

Naamruimten worden vaak gedaan door te doen:

stack_push(thing *)

in plaats van

stack::push(thing *)

Een Cstruct maken in iets als een C++klasse die je kunt draaien:

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

Into

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

En doe:

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
}

Ik heb de destructor niet gedaan of verwijderd, maar het volgt hetzelfde patroon.

this_is_here_as_an_example_only is als een statische klassevariabele — gedeeld door alle instanties van een type. Alle methoden zijn echt statisch, behalve dat sommige een dit *

. nemen


Antwoord 4, autoriteit 15%

Ik geloof dat het implementeren van OOP in C niet alleen op zichzelf nuttig is, maar ook een uitstekende manier is om OOP te lerenen de innerlijke werking ervan te begrijpen. De ervaring van veel programmeurs heeft geleerd dat om een techniek efficiënt en zelfverzekerd te gebruiken, een programmeur moet begrijpen hoe de onderliggende concepten uiteindelijk worden geïmplementeerd. Het emuleren van klassen, overerving en polymorfisme in C leert precies dit.

Om de oorspronkelijke vraag te beantwoorden, volgen hier een paar bronnen die u leren hoe u OOP in C kunt doen:

EmbeddedGurus.com blogpost “Object-based programming in C” laat zien hoe klassen en enkele overerving in portable C kunnen worden geïmplementeerd:
http://embeddedgurus.com/state-space/ 2008/01/object-based-programmering-in-c/

Applicatienotitie “”C+”—Objectgeoriënteerd programmeren in C” laat zien hoe u klassen, enkele overerving en late binding (polymorfisme) in C implementeert met behulp van preprocessormacro’s:
http://www.state-machine.com/resources/cplus_3.0_manual.pdf , de voorbeeldcode is beschikbaar op http://www.state- machine.com/resources/cplus_3.0.zip


Antwoord 5, autoriteit 8%

Ik heb het zien gebeuren. Ik zou het niet aanraden. C++ begon oorspronkelijk op deze manier als een preprocessor die C-code produceerde als tussenstap.

Wat u uiteindelijk doet, is een verzendtabel maken voor al uw methoden waarin u uw functieverwijzingen opslaat. Het afleiden van een klasse zou inhouden dat je deze verzendtabel kopieert en de items vervangt die je wilde overschrijven, waarbij je nieuwe “methoden” de originele methode moeten aanroepen als het de basismethode wil aanroepen. Uiteindelijk herschrijf je C++.


Antwoord 6, autoriteit 7%

Natuurlijk is dat mogelijk. Dit is wat GObject, het raamwerk dat alle GTK+en GNOMEis gebaseerd op, doet.


Antwoord 7, autoriteit 5%

De subbibliotheek C stdio FILE is een uitstekend voorbeeld van hoe u abstractie, inkapseling en modulariteit kunt creëren in onvervalst C.

Overerving en polymorfisme – de andere aspecten die vaak als essentieel worden beschouwd voor OOP – bieden niet noodzakelijk de productiviteitswinst die ze beloven en redelijkargumentenzijn naar voren gebrachtdie de ontwikkeling en het nadenken over het probleemdomein.


Antwoord 8, autoriteit 4%

Een triviaal voorbeeld met een dier en een hond: je spiegelt het vtable-mechanisme van C++ (grotendeels in ieder geval). U scheidt ook toewijzing en instantie (Animal_Alloc, Animal_New), zodat we malloc() niet meerdere keren aanroepen. We moeten ook expliciet de this-aanwijzer doorgeven.

Als je niet-virtuele functies zou doen, is dat trival. Je voegt ze gewoon niet toe aan de vtable en statische functies hebben geen thispointer nodig. Meervoudige overerving vereist over het algemeen meerdere vtables om dubbelzinnigheden op te lossen.

Je zou ook setjmp/longjmp moeten kunnen gebruiken om uitzonderingen af te handelen.

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. Dit is getest op een C++-compiler, maar het zou eenvoudig moeten zijn om het te laten werken op een C-compiler.


Antwoord 9, autoriteit 3%

Bekijk GObject. Het is bedoeld als OO in C en een implementatie van wat je zoekt. Als je echter echt OO wilt, ga dan voor C++ of een andere OOP-taal. GObject kan soms erg moeilijk zijn om mee te werken als je gewend bent om met OO-talen om te gaan, maar zoals met alles zul je wennen aan de conventies en flow.


Antwoord 10, autoriteit 3%

Dit is interessant om te lezen. Ik heb zelf nadenken over dezelfde vraag, en de voordelen van het denken over het is dit:

  • Probeer je voor te stellen hoe je OOP-concepten in een niet-OOP-taal kunt implementeren, helpt me de sterke punten van de OOP-taal te begrijpen (in mijn geval, C++). Dit helpt me een beter oordeel te geven over het gebruik van C of C++ voor een bepaald type applicatie – waar de voordelen van één buiten de ander weegt.

  • In mijn browsen op het web voor informatie en meningen hierover vond ik een auteur die code voor een ingesloten processor schrijft en alleen een C-compiler had beschikbaar:
    http: // www. Eetimes.com/discussion/oWGE / 4024626/Object-Oriënt-c-c-trek-foundation-classes-part-1

In zijn geval was het analyseren en aanpassen van OOP-concepten in duidelijke C een geldige achtervolging. Het lijkt erop dat hij open was om wat OOP-concepten te offeren vanwege de prestatie-boven-head-hit die resulteert uit een poging om ze te implementeren in c.

De les die ik heb genomen is, ja het kan tot op zekere hoogte worden gedaan, en ja, er zijn enkele goede redenen om het te proberen.

Uiteindelijk is het apparaat tweevoudig stapelbewijsstukken, waardoor de programmeercounter in de buurt is en het berekenen van geheugentoegangsoperaties. Vanuit het efficiëntiepunt, hoe minder van deze berekeningen door uw programma, des te beter … maar soms moeten we deze belasting eenvoudig betalen, zodat we ons programma kunnen organiseren op een manier die het minst vatbaar maakt voor menselijke fouten. De OOP-taalcompiler streeft ernaar beide aspecten te optimaliseren. De programmeur moet veel zorgvuldig voorzichtig zijn met het implementeren van deze concepten in een taal zoals c.


Antwoord 11, Autoriteit 3%

Er zijn verschillende technieken die kunnen worden gebruikt. De belangrijkste is meer hoe het project te splitsen. We gebruiken een interface in ons project die wordt gedeclareerd in een .h-bestand en de implementatie van het object in een .c-bestand. Het belangrijkste is dat alle modules die het .h-bestand bevatten, alleen een object als een void *zien, en dat het .c-bestand de enige module is die de binnenkant van de structuur kent.

Zoiets voor een klas noemen we FOO als voorbeeld:

In het .h-bestand

#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

Het C-implementatiebestand zal er ongeveer zo uitzien.

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

Dus ik geef de aanwijzer expliciet naar een object aan elke functie van die module. Een C++ compiler doet het impliciet, en in C schrijven we het expliciet uit.

Ik gebruik thisecht in mijn programma’s om ervoor te zorgen dat mijn programma niet compileert in C++, en het heeft de fijne eigenschap dat het in een andere kleur staat in mijn editor voor syntaxisaccentuering.

De velden van de FOO_struct kunnen in de ene module worden gewijzigd en een andere module hoeft niet eens opnieuw te worden gecompileerd om nog steeds bruikbaar te zijn.

Met die stijl behandel ik al een groot deel van de voordelen van OOP (data-encapsulatie). Door functieaanwijzers te gebruiken, is het zelfs gemakkelijk om iets als overerving te implementeren, maar eerlijk gezegd is het maar zelden nuttig.


Antwoord 12, autoriteit 3%

Misschien vindt u het nuttig om de documentatie van Apple te bekijken voor de Core Foundation-set API’s. Het is een pure C API, maar veel van de typen zijn overbrugd naar Objective-C-objectequivalenten.

Misschien vindt u het ook nuttig om naar het ontwerp van Objective-C zelf te kijken. Het is een beetje anders dan C++ omdat het objectsysteem is gedefinieerd in termen van C-functies, b.v. objc_msg_sendom een methode op een object aan te roepen. De compiler vertaalt de syntaxis van vierkante haakjes naar die functieaanroepen, dus u hoeft het niet te weten, maar gezien uw vraag kan het nuttig zijn om te leren hoe het onder de motorkap werkt.


Antwoord 13, autoriteit 2%

Je kunt het vervalsen met functieaanwijzers, en in feite denk ik dat het theoretisch mogelijk is om C++-programma’s in C te compileren.

Het heeft echter zelden zin om een paradigma op te leggen aan een taal in plaats van een taal te kiezen die een paradigma gebruikt.


Antwoord 14, autoriteit 2%

Objectgeoriënteerde C, kan worden gedaan, ik heb dat type code in productie gezien in Korea, en het was het meest verschrikkelijke monster dat ik in jaren had gezien (dit was net als vorig jaar (2007) dat ik de code).
Dus ja, het kan worden gedaan, en ja, mensen hebben het eerder gedaan, en doen het nog steeds, zelfs in deze tijd. Maar ik zou C++ of Objective-C aanraden, beide zijn talen geboren uit C, met als doel objectoriëntatie te bieden met verschillende paradigma’s.


Antwoord 15, autoriteit 2%

Als je ervan overtuigd bent dat een OOP-aanpak superieur is voor het probleem dat je probeert op te lossen, waarom zou je dan proberen het op te lossen met een niet-OOP-taal? Het lijkt erop dat u het verkeerde gereedschap voor de klus gebruikt. Gebruik C++ of een andere objectgeoriënteerde C-varianttaal.

Als je dit vraagt omdat je begint te coderen op een reeds bestaand groot project geschreven in C, dan moet je niet proberen je eigen (of iemand anders) OOP-paradigma’s in de infrastructuur van het project te forceren. Volg de richtlijnen die al aanwezig zijn in het project. Over het algemeen zullen schone API’s en geïsoleerde bibliotheken en modules een heel eind bijdragen aan een schoon OOP-achtig-ontwerp.

Als je na dit alles echt OOP C wilt gaan doen, lees dan deze(PDF).


Antwoord 16, autoriteit 2%

Ja, dat kan. Mensen schreven objectgeoriënteerde C voordat C++ of Objective-Cop het toneel verscheen. Zowel C++ als Objective-C waren, gedeeltelijk, pogingen om enkele van de OO-concepten die in C werden gebruikt te nemen en ze te formaliseren als onderdeel van de taal.

Hier is een heel eenvoudig programma dat laat zien hoe je iets kunt maken dat eruitziet als een methodeaanroep (er zijn betere manieren om dit te doen. Dit is slechts het bewijs dat de taal de concepten ondersteunt):

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

Antwoord 17, Autoriteit 2%

Natuurlijk zal het gewoon niet zo mooi zijn als het gebruik van een taal met ingebouwde ondersteuning. Ik heb zelfs ‘object-georiënteerde assembler’ geschreven.


Antwoord 18, Autoriteit 2%

Een kleine OOC-code om toe te voegen:

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

Antwoord 19

Ik ben dit voor één jaar opgraven:

Naarmate het GOBJECT-systeem moeilijk te gebruiken is met Pure C, probeerde ik een aantal mooie macro’s te schrijven om de OO-stijl te vergemakkelijken met 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;
}

Hier is mijn projectsite (ik heb niet genoeg tijd om nl. doc te schrijven, maar de doc in het Chinees is veel beter).

OOC-GCC


Antwoord 20

Er is een voorbeeld van overerving waarbij C wordt gebruikt in de toespraak van Jim Larson uit 1996 in Sectie 312 Lunchtijdseminar programmerenhier: Hoog en laag niveau C.


Antwoord 21

Welke artikelen of boeken zijn goed om OOP-concepten in C te gebruiken?

Dave Hanson’s C-interfaces en implementatiesis uitstekendover inkapseling en naamgeving en zeer goed in het gebruik van functiewijzers. Dave probeert geen overerving te simuleren.


Antwoord 22

OOP is slechts een paradigma dat data belangrijker vindt dan code in programma’s. OOP is geen taal. Dus, zoals gewone C een eenvoudige taal is, is OOP in gewone C ook eenvoudig.


Antwoord 23

Eén ding dat u misschien wilt doen, is kijken naar de implementatie van de Xt-toolkit voor X Window. Natuurlijk wordt het lang in de tand, maar veel van de gebruikte structuren zijn ontworpen om op een OO-manier te werken binnen de traditionele C. Over het algemeen betekent dit het toevoegen van een extra laag indirecte hier en daar en het ontwerpen van structuren om over elkaar heen te leggen.

Je kunt op deze manier echt veel doen op het gebied van OO in C, ook al voelt het soms zo, OO-concepten kwamen niet volledig voort uit de geest van #include<favorite_OO_Guru.h>. Ze vormden echt veel van de gevestigde beste praktijken van die tijd. OO-talen en -systemen hebben slechts delen van de programmeertijdgeest van de dag gedistilleerd en versterkt.


Antwoord 24

Het antwoord op de vraag is ‘Ja, dat kan’.

Object-georiënteerde C (OOC)-kit is voor degenen die op een object-georiënteerde manier willen programmeren, maar ook vasthouden aan de goede oude C. OOC implementeert klassen, enkele en meervoudige overerving, afhandeling van uitzonderingen.

Functies

• Gebruikt alleen C-macro’s en -functies, geen taaluitbreidingen vereist! (ANSI-C)

• Gemakkelijk te lezen broncode voor uw toepassing. Er is voor gezorgd dat alles zo eenvoudig mogelijk is.

• Enkele overerving van klassen

• Meervoudige overerving door interfaces en mixins (sinds versie 1.3)

• Uitzonderingen implementeren (in pure C!)

• Virtuele functies voor klassen

• Externe tool voor eenvoudige implementatie van klassen

Ga voor meer informatie naar http://ooc-coding.sourceforge.net/.


Antwoord 25

Het lijkt erop dat mensen proberen de C++ -stijl te emuleren met behulp van C. Mijn take is dat het doen van objectgerichte programmering C echt struct-georiënteerde programmering doet. U kunt echter dingen bereiken zoals late binding, inkapseling en erfenis. Voor erfenis definieert u expliciet een aanwijzer naar de basissterkten in uw substructuur en dit is uiteraard een vorm van meerdere erfenis. U moet ook bepalen of uw

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

Compileer met c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj.

Dus het advies is om vast te houden aan een pure C-stijl en niet te proberen een C++-stijl te forceren. Deze manier leent zich ook voor een zeer schone manier om een API te bouwen.


Antwoord 26

Zie http://slkpg.byethost7.com/instance.htmlvoor nog een andere wending op OOP in C. Het benadrukt instantiegegevens voor herintreding met alleen native C. Meerdere overerving wordt handmatig gedaan met behulp van functie-wrappers. Type veiligheid wordt gehandhaafd. Hier is een klein voorbeeld:

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

Antwoord 27

Ik ben een beetje laat voor het feest, maar ik wil mijn ervaring over het onderwerp delen: ik werk tegenwoordig met embedded dingen, en de enige (betrouwbare) compiler die ik heb is C, dus ik wil toepassen objectgeoriënteerde benadering in mijn embedded projecten geschreven in C.

De meeste oplossingen die ik tot nu toe heb gezien, maken veel gebruik van typecasts, dus we verliezen typeveiligheid: compiler zal je niet helpen als je een fout maakt. Dit is volkomen onaanvaardbaar.

Vereisten die ik heb:

  • Vermijd typecasts zoveel mogelijk, zodat we de typeveiligheid niet verliezen;
  • Polymorfisme: we zouden virtuele methoden moeten kunnen gebruiken, en de gebruiker van de klasse zou niet moeten weten of een bepaalde methode virtueel is of niet;
  • Meervoudige overerving: ik gebruik het niet vaak, maar soms wil ik echt dat een klasse meerdere interfaces implementeert (of meerdere superklassen uitbreidt).

Ik heb mijn aanpak in detail uitgelegd in dit artikel: Objectgeoriënteerd programmeren in C; plus, er is een hulpprogramma voor het automatisch genereren van boilerplate-code voor basis- en afgeleide klassen.


Antwoord 28

Ik heb een kleine bibliotheek gebouwd waarin ik dat heb geprobeerd en voor mij werkt het heel goed. Dus ik dacht dat ik de ervaring deel.

https://github.com/thomasfuhringer/oxygen

Enkele overerving kan vrij eenvoudig worden geïmplementeerd door een struct te gebruiken en deze uit te breiden voor elke andere onderliggende klasse. Een eenvoudige cast naar de bovenliggende structuur maakt het mogelijk om bovenliggende methoden op alle nakomelingen te gebruiken.
Zolang je weet dat een variabele verwijst naar een struct die dit soort object bevat, kun je altijd casten naar de rootklasse en aan introspectie doen.

Zoals genoemd is, zijn virtuele methoden enigszins lastiger. Maar ze zijn uitvoerbaar. Om dingen eenvoudig te houden, gebruik ik gewoon een reeks functies in de klasse omschrijvingstructuur die elke kinderklasse kopieert en individuele slots herstelt waar nodig.

Meerdere erfenis zou nogal gecompliceerd zijn om te implementeren en komt met een aanzienlijke prestatie-impact. Dus ik laat het. Ik beschouw het als wenselijk en nuttig in een heel paar gevallen om de reinigingsmodel van de omstandigheden van het echte leven netjes te modelleren, maar in waarschijnlijk 90% van de gevallen dekt Single InHeritance de behoeften. En enkele erfenis is eenvoudig en kost niets.

Ik geef ook niet om het type veiligheid. Ik denk dat je niet van de compiler moet hangen om te voorkomen dat je fouten programmeert. En het beschermt u alleen van een nogal klein deel van fouten.

Typisch, in een objectgeoriënteerde omgeving wilt u ook referentietellen implementeren om het geheugenbeheer voor zover mogelijk te automatiseren. Dus stel ik ook een referentietelling in de rootklasse “Object” en een of andere functionaliteit om toewijzing en deallocatie van heapgeheugen in te loggen.

Het is allemaal heel eenvoudig en mager en geeft me de essentie van OO zonder me te dwingen om te gaan met het monster dat C++ is. En ik behoud de flexibiliteit van het verblijf in C-land, wat onder andere het gemakkelijker maakt om bibliotheken van derden te integreren.


Antwoord 29

Ik stel voor om doelstelling-C te gebruiken, een superset van C.

Hoewel DOGELIJKE-C 30 jaar oud is, maakt het de mogelijkheid om een ​​elegante code te schrijven.

http://en.wikipedia.org/wiki/objective-c


ANTWOORD 30

Ja, het is mogelijk.

Dit is pure C, geen voorbewerking van macro’s. Het heeft overerving, polymorfisme, gegevensinkapseling (inclusief privégegevens). Het heeft geen gelijkwaardige beschermde kwalificatie, wat betekent dat privégegevens ook privé zijn in de overervingsketen.

#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);
}
/*output:
6.56
13.12
*/

Other episodes