Herencia de estructuras en C

11 minutos de lectura

¿Puedo heredar una estructura en C? Si es así, ¿cómo?

  • No existe tal cosa como la herencia en C

    – Ed S.

    11 de julio de 2009 a las 18:30

  • … A menos que lo implementes.

    – Felipe

    20 de marzo de 2013 a las 15:28

avatar de usuario
Daniel Earwicker

Lo más cercano que puede obtener es el idioma bastante común:

typedef struct
{
    // base members

} Base;

typedef struct
{
    Base base;

    // derived members

} Derived;

Como Derived comienza con una copia de BasePuedes hacerlo:

Base *b = (Base *)d;

Donde d es una instancia de Derived. Así que son algo polimórficos. Pero tener métodos virtuales es otro desafío: para hacerlo, necesitaría tener el equivalente de un puntero vtable en Baseque contiene punteros de función a funciones que aceptan Base como su primer argumento (que podría nombrar this).

¡En ese momento, también puede usar C ++!

  • ¡Bueno, eso suponiendo que haya un compilador de C++ disponible para su plataforma!

    luego

    13 de julio de 2009 a las 1:22

  • Si un compilador de C está disponible, entonces también lo está un compilador de C++; solo use uno que produzca C como salida.

    – Daniel Earwicker

    13 de julio de 2009 a las 6:29

  • Uf… me salvaste la vida. Por lo general, codifico en Java y cuando me enfrenté al código similar al que publicaste, pensé que era una composición y me confundí muchísimo cuando lo lanzaron.

    – Surya Wijaya Madjid

    11 de septiembre de 2012 a las 11:57

  • Verás usos de eso en proyectos reales complejos (como git). Ver log-tree.c. etiqueta de estructura “hereda de objeto de estructura

    – albfán

    06/04/2013 a las 23:36


  • Disculpe, ¿cómo es esto? Base *b = (Base *)d; posible. uno es un Derived* y el otro es un Base*. Ya que Derived tendría más miembros, los punteros son diferentes. ¿Cómo puedes hacer ese reparto?

    -Doga Oruc

    10 de septiembre de 2020 a las 12:35

C no tiene un concepto explícito de herencia, a diferencia de C++. Sin embargo, puede reutilizar una estructura en otra estructura:

typedef struct {
    char name[NAMESIZE];
    char sex;
} Person;

typedef struct {
    Person person;
    char job[JOBSIZE];
} Employee;

typedef struct {
    Person person;
    char booktitle[TITLESIZE];
} LiteraryCharacter;

  • Hasta donde yo sé, también puede tener un miembro de estructura/clase dentro de otro en C++.

    – Tyler Millican

    11 de julio de 2009 a las 18:36

  • C dice que no aparece relleno antes del primer miembro de una estructura. Entonces, de hecho, puede (y está permitido) convertir LiteraryCharacter* a Person* y tratarlo como una persona. +1

    – Johannes Schaub – litb

    12 de julio de 2009 a las 1:06

  • @JohannesSchaub-litb su comentario fue una mejor explicación que la respuesta en sí 🙂

    – Greg

    15 de febrero de 2012 a las 11:49

  • Es importante tener en cuenta que solo debe pasar estos tipos como referencia. No puede copiarlos en un objeto Person o se empalmará.

    –Kevin Cox

    16/03/2014 a las 17:35

Me gusta y usé la idea de Herencia segura de tipos en C.

Por ejemplo:

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;

  • Nunca pensé en esto. Y si haces que la unión sea anónima, es bastante ordenado. Sin embargo, la desventaja es que debe enumerar todos los padres para evitar variables anidadas.

    –Kevin Cox

    16 de marzo de 2014 a las 17:34

  • Enfoque interesante. Sin embargo, tan pronto como escriba leopard.basese elimina todo el punto de herencia/polimorfismo.

    – Powerslave

    9 de abril de 2015 a las 9:21

  • ¿No tienes un problema de herencia de diamantes aquí? leopard.base.felidae.base.animal.weight y leopard.base.animal.weight?

    – Alexander Torstling

    15 de febrero de 2016 a las 23:01

  • @Alexander Torstling no, no lo harás. leopard.base.felidae.base.animal.weight es solo otro nombre para leopard.base.animal.weight – es la misma cosa en el mismo lugar en la memoria.

    – Martín

    21/02/2016 a las 21:30

  • Esto se ve genial. Gracias.

    – PSkocik

    15 de diciembre de 2016 a las 21:16

Si su compilador admite estructuras anónimas, puede hacer esto:

typedef struct Base
{
    // base members
} Base_t;

typedef struct
{
   struct Base;  //anonymous struct

   // derived members

} Derived_t;

De esta manera, se puede acceder directamente a los miembros de la estructura base, lo cual es más agradable.

Si desea usar algo de magia gcc (que supongo que funcionaría con el compilador C de Microsoft), puede hacer algo como:


struct A
{
   int member1;
};

struct B
{
   struct A;
   int member2;
}

Con gcc puede compilar esto con -fms-extensions (permite miembros de estructura sin nombre como lo hace el compilador de Microsoft). Esto es similar a la solución dada por Daniel Earwicker excepto que le permite acceder a miembro1 en una instancia de estructura B. es decir, B.member1 en lugar de BAmember1.

Este probablemente no sea el enfoque más portátil y no funcionará si se usa un compilador de C++ (la semántica de diferentes idiomas significa que está redeclarando/definiendo la estructura A en lugar de instanciarla).

Sin embargo, si vives en la tierra gcc/C solo funcionará y hará exactamente lo que quieres.

  • ¿No es esta composición?

    – Sumit Gera

    20 de septiembre de 2013 a las 20:08

  • No, es una herencia adecuada. Suponiendo que tiene una estructura de tipo estructura B denominada b, b.member1 se compilará y funcionará como cabría esperar. La composición sería algo así como b.base.member1. GCC realiza esta magia por ti. En realidad, tiene la definición de estructura B como dos enteros en este caso.

    – Mate

    21 de septiembre de 2013 a las 1:11

  • ¿Es esto posible solo en C, no en C++? Si no, por favor visite Esta.

    – Sumit Gera

    21 de septiembre de 2013 a las 8:53


  • Es solo C. La sintaxis es ilegal en C++. Aunque C ++ tiene una herencia adecuada de estructuras al igual que las clases.

    – Mate

    21 de septiembre de 2013 a las 12:55

  • ¡Ahora me siento mucho mejor!

    – Sumit Gera

    21 de septiembre de 2013 a las 15:44


avatar de usuario
Ziezi

Puedes hacer lo mencionado anteriormente

typedef struct
{
    // base members

} Base;

typedef struct
{
    Base base;

    // derived members

} Derived;

Pero si desea evitar la conversión de punteros, puede utilizar punteros a un union de Base y Derived.

  • ¿No es esta composición?

    – Sumit Gera

    20 de septiembre de 2013 a las 20:08

  • No, es una herencia adecuada. Suponiendo que tiene una estructura de tipo estructura B denominada b, b.member1 se compilará y funcionará como cabría esperar. La composición sería algo así como b.base.member1. GCC realiza esta magia por ti. En realidad, tiene la definición de estructura B como dos enteros en este caso.

    – Mate

    21 de septiembre de 2013 a las 1:11

  • ¿Es esto posible solo en C, no en C++? Si no, por favor visite Esta.

    – Sumit Gera

    21 de septiembre de 2013 a las 8:53


  • Es solo C. La sintaxis es ilegal en C++. Aunque C ++ tiene una herencia adecuada de estructuras al igual que las clases.

    – Mate

    21 de septiembre de 2013 a las 12:55

  • ¡Ahora me siento mucho mejor!

    – Sumit Gera

    21 de septiembre de 2013 a las 15:44


Esto funciona compilando con -fms-extensions

Imagen del diagrama

C Principal

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

}

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

ResumenProducto.h

#ifndef AbstractProduct_H
#define AbstractProduct_H

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/***********************/

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

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

Libro.h

#ifndef Book_H
#define Book_H

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#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

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

Producto.h

#ifndef Product_H
#define Product_H

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#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 <stdlib.h>
#include <stdio.h>
#include <string.h>

#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

  • bueno para la prueba de concepto, pero si tiene que hacer todo este camino para lograr OOP en C, estaría mejor con C ++. En mi humilde opinión, C no está diseñado para OOP y si realmente necesita ser OOP puro (con mucho esfuerzo para lograrlo), está usando el lenguaje incorrecto. Por mi parte, no me gustaría mantener dicho código.

    – Alex

    12/04/2016 a las 16:53

  • Además, con este enfoque creo que estás desperdiciando algo de memoria. A diferencia de C++, habrá un tamaño adicional de (puntero de función) * número de métodos para cada instancia. En C++, sizeof(class) no incluye los punteros de método y, en caso de que haya métodos virtuales, contiene 1 puntero adicional a vtable.

    – Alex

    29 de junio de 2016 a las 20:57

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad