Struct Inheritance in C

Posso ereditare una struttura in C? Se sì, come?

Il più vicino che puoi ottenere è l’idioma abbastanza comune:

typedef struct { // base members } Base; typedef struct { Base base; // derived members } Derived; 

Siccome Derived inizia con una copia di Base , puoi farlo:

 Base *b = (Base *)d; 

Dove d è un’istanza di Derived . Quindi sono un po ‘polimorfici. Ma avere i metodi virtuali è un’altra sfida: per farlo, è necessario avere l’equivalente di un puntatore vtable in Base , contenente i puntatori di funzioni alle funzioni che accettano Base come primo argomento (che si potrebbe chiamare this ).

A quel punto, potresti anche usare C ++!

C non ha un concetto esplicito di ereditarietà, diversamente dal C ++. Tuttavia, puoi riutilizzare una struttura in un’altra struttura:

 typedef struct { char name[NAMESIZE]; char sex; } Person; typedef struct { Person person; char job[JOBSIZE]; } Employee; typedef struct { Person person; char booktitle[TITLESIZE]; } LiteraryCharacter; 

Mi piace e ho usato l’idea dell’ereditarietà Typesafe in C.

Per esempio:

 struct Animal { int weight; }; struct Felidae { union { struct Animal animal; } base; int furLength; }; struct Leopard { union { struct Animal animal; struct Felidae felidae; } base; int dotCounter; }; 

Uso:

 struct Leopard leopard; leopard.base.animal.weight = 44; leopard.base.felidae.furLength = 2; leopard.dotCounter = 99; 

Se il tuo compilatore supporta le strutture anonime, puoi farlo:

 typedef struct Base { // base members } Base_t; typedef struct { struct Base; //anonymous struct // derived members } Derived_t; 

In questo modo, i membri di stent di base possono essere inseriti direttamente, il che è più bello.

Se vuoi usare qualche magia gcc (che presumerei possa funzionare con il compilatore C di Microsoft) puoi fare qualcosa del tipo:

 struct A { int member1; }; struct B { struct A; int member2; } 

Con gcc puoi compilarlo con -fms-extensions (Permette i membri della struct senza nome come fa il compilatore di Microsofts). Questo è simile alla soluzione fornita da Daniel Earwicker, tranne per il fatto che consente di accedere a memeber1 su un’istanza di struct B. cioè B.member1 invece di BAmember1.

Questo probabilmente non è l’approccio più portabile e non funzionerà se si utilizza un compilatore C ++ (la semantica di un linguaggio diverso significa che sta ridichiando / definendo la struct A invece di istanziarla).

Se invece vivi nella terra di gcc / C, funzionerà e farà esattamente ciò che desideri.

Questo funziona compilando con -fms-extensions

Immagine del diagramma

main.c

 #include "AbstractProduct.h" #include "Book.h" #include "Product.h" #include "TravelGuide.h" /***********************/ int main() { Product p = Product_new(); p.set_id(&p, 2); p.set_name(&p, "name2"); p.set_description(&p, "description2"); p.set_price(&p, 2000); p.display(&p); TravelGuide tg = TravelGuide_new(); tg.set_id(&tg, 1); tg.set_name(&tg, "name1"); tg.set_description(&tg, "description1"); tg.set_price(&tg, 1000); tg.set_isbn(&tg, "isbn1"); tg.set_author(&tg, "author1"); tg.set_title(&tg, "title1"); tg.set_country(&tg, "country1"); tg.display(&tg); } 

AbstractProduct.c

 #include "AbstractProduct.h" /*-------------------------------*/ static void set_id(AbstractProduct *this, int id) { this->id = id; } /*-------------------------------*/ static void set_name(AbstractProduct *this, char *name) { strcpy(this->name, name); } /*-------------------------------*/ static void set_description(AbstractProduct *this, char *description) { strcpy(this->description, description); } /*-------------------------------*/ static int get_id(AbstractProduct *this) { return this->id; } /*-------------------------------*/ static char *get_name(AbstractProduct *this) { return this->name; } /*-------------------------------*/ static char *get_description(AbstractProduct *this) { return this->description; } /*-------------------------------*/ static void display(AbstractProduct *this) { printf("-AbstractProduct- \n"); printf("id: %d\n", this->get_id(this)); printf("name: %s\n", this->get_name(this)); printf("description: %s\n", this->get_description(this)); printf("\n"); } /*-------------------------------*/ void AbstractProduct_init(AbstractProduct *obj) { obj->set_id = set_id; obj->set_name = set_name; obj->set_description = set_description; obj->get_id = get_id; obj->get_name = get_name; obj->get_description = get_description; obj->display = display; } /*-------------------------------*/ AbstractProduct AbstractProduct_new() { AbstractProduct aux; AbstractProduct_init(&aux); return aux; } 

AbstractProduct.h

 #ifndef AbstractProduct_H #define AbstractProduct_H #include  #include  #include  /***********************/ typedef struct AbstractProduct{ int id; char name[1000]; char description[1000]; void (*set_id)(); void (*set_name)(); void (*set_description)(); int (*get_id)(); char *(*get_name)(); char *(*get_description)(); void (*display)(); } AbstractProduct; AbstractProduct AbstractProduct_new(); void AbstractProduct_init(AbstractProduct *obj); #endif 

Book.c

 #include "Book.h" /*-------------------------------*/ static void set_isbn(Book *this, char *isbn) { strcpy(this->isbn, isbn); } /*-------------------------------*/ static void set_author(Book *this, char *author) { strcpy(this->author, author); } /*-------------------------------*/ static void set_title(Book *this, char *title) { strcpy(this->title, title); } /*-------------------------------*/ static char *get_isbn(Book *this) { return this->isbn; } /*-------------------------------*/ static char *get_author(Book *this) { return this->author; } /*-------------------------------*/ static char *get_title(Book *this) { return this->title; } /*-------------------------------*/ static void display(Book *this) { Product p = Product_new(); p.display(this); printf("-Book- \n"); printf("isbn: %s\n", this->get_isbn(this)); printf("author: %s\n", this->get_author(this)); printf("title: %s\n", this->get_title(this)); printf("\n"); } /*-------------------------------*/ void Book_init(Book *obj) { Product_init((Product*)obj); obj->set_isbn = set_isbn; obj->set_author = set_author; obj->set_title = set_title; obj->get_isbn = get_isbn; obj->get_author = get_author; obj->get_title = get_title; obj->display = display; } /*-------------------------------*/ Book Book_new() { Book aux; Book_init(&aux); return aux; } 

Book.h

 #ifndef Book_H #define Book_H #include  #include  #include  #include "Product.h" /***********************/ typedef struct Book{ Product; char isbn[1000]; char author[1000]; char title[1000]; void (*set_isbn)(); void (*set_author)(); void (*set_title)(); char *(*get_isbn)(); char *(*get_author)(); char *(*get_title)(); // void (*display)(); } Book; Book Book_new(); void Book_init(Book *obj); #endif 

Product.c

 #include "Product.h" /*-------------------------------*/ static void set_price(Product *this, double price) { this->price = price; } /*-------------------------------*/ static double get_price(Product *this) { return this->price; } /*-------------------------------*/ static void display(Product *this) { AbstractProduct p = AbstractProduct_new(); p.display(this); printf("-Product- \n"); printf("price: %f\n", this->get_price(this)); printf("\n"); } /*-------------------------------*/ void Product_init(Product *obj) { AbstractProduct_init((AbstractProduct*)obj); obj->set_price = set_price; obj->get_price = get_price; obj->display = display; } /*-------------------------------*/ Product Product_new() { Product aux; Product_init(&aux); return aux; } 

Product.h

 #ifndef Product_H #define Product_H #include  #include  #include  #include "AbstractProduct.h" /***********************/ typedef struct Product{ AbstractProduct; double price; void (*set_price)(); double (*get_price)(); // void (*display)(); } Product; Product Product_new(); void Product_init(Product *obj); #endif 

TravelGuide.c

 #include "TravelGuide.h" /*-------------------------------*/ static void set_country(TravelGuide *this, char *country) { strcpy(this->country, country); } /*-------------------------------*/ static char *get_country(TravelGuide *this) { return this->country; } /*-------------------------------*/ static void display(TravelGuide *this) { Book b = Book_new(); b.display(this); printf("-TravelGuide- \n"); printf("country: %s\n", this->get_country(this)); printf("\n"); } /*-------------------------------*/ void TravelGuide_init(TravelGuide *obj) { Book_init((Book*)obj); obj->set_country = set_country; obj->get_country = get_country; obj->f = obj->display; obj->display = display; } /*-------------------------------*/ TravelGuide TravelGuide_new() { TravelGuide aux; TravelGuide_init(&aux); return aux; } 

TravelGuide.h

 #ifndef TravelGuide_H #define TravelGuide_H #include  #include  #include  #include "Book.h" /***********************/ typedef struct TravelGuide{ Book; char country[1000]; void (*f)(); void (*set_country)(); char *(*get_country)(); // void *(*display)(); } TravelGuide; TravelGuide TravelGuide_new(); void TravelGuide_init(TravelGuide *obj); #endif 

Makefile

 .PHONY: clean define ANNOUNCE_BODY *********************************************** ************ start make ************** *********************************************** endef all: $(info $(ANNOUNCE_BODY)) clear; if [ -f binary/main ]; then rm binary/main; fi; # compiler gcc $(INC) -c -fms-extensions main.c -o binary/main.o gcc $(INC) -c -fms-extensions AbstractProduct.c -o binary/AbstractProduct.o gcc $(INC) -c -fms-extensions Product.c -o binary/Product.o gcc $(INC) -c -fms-extensions Book.c -o binary/Book.o gcc $(INC) -c -fms-extensions TravelGuide.c -o binary/TravelGuide.o # linker gcc binary/main.o \ binary/AbstractProduct.o \ binary/Product.o \ binary/Book.o \ binary/TravelGuide.o \ -o \ binary/main 

Una leggera variazione della risposta di anon (e altri simili). Per l’ereditarietà di un livello si può fare quanto segue:

 #define BASEFIELDS \ char name[NAMESIZE]; \ char sex typedef struct { BASEFIELDS; } Person; typedef struct { BASEFIELDS; char job[JOBSIZE]; } Employee; typedef struct { BASEFIELDS; Employee *subordinate; } Manager; 

In questo modo le funzioni che accettano il puntatore a Persona, accetteranno il puntatore a Dipendente o Manager (con cast), come in altre risposte, ma in questo caso l’inizializzazione sarà anche naturale:

 Employee e = { .name = "..."; ... }; 

vs

 # as in anon's answer Employee e = { .person.name = "..."; ... }; 

Credo che questo sia il modo in cui alcuni progetti popolari lo fanno (per esempio libuv)

AGGIORNAMENTO: inoltre ci sono alcuni buoni esempi di concetto simile (ma non lo stesso) nell’implementazione di eventi libsdl usando structs e unions.

Puoi fare quanto sopra menzionato

 typedef struct { // base members } Base; typedef struct { Base base; // derived members } Derived; 

Ma se vuoi evitare il puntatore, puoi usare i puntatori a union di Base e Derived .

C non è un linguaggio orientato agli oggetti e quindi non ha ereditarietà.

Puoi simularlo, ma non puoi realmente ereditare.

No, non puoi. il miglior approccio a OOP in C sta usando ADT .

No, non puoi. C non supporta il concetto di ereditarietà.