2009-07-11 45 views

Respuesta

-4

No, no puedes. C no es compatible con el concepto de herencia.

+8

no es compatible, pero tampoco se interpone en el camino. – Javier

-2

C no es un lenguaje orientado a objetos y por lo tanto no tiene herencia.

-2

Puede simularlo, pero no puede realmente heredar.

+3

¿Qué es _reality_? C++ es simplemente una biblioteca de tiempo de ejecución muy simple para despachar y mucha sintaxis de compilación para llamarla cuando sea necesario. los compiladores originales de C++ producían código C, después de todo. (y de hecho muy legible C) – Javier

+4

Mientras tanto, otros usuarios han mostrado CÓMO hacer esto. – luiscubal

38

C no tiene un concepto explícito de la 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; 
+1

Por lo que yo sé, puede tener un miembro struct/class dentro de otro en C++ también. –

+4

Por supuesto que puedes. –

+42

C dice que no aparece ningún relleno antes del primer miembro de una estructura. Así que de hecho (y está permitido) emitir LiteraryCharacter * a Person *, y tratarlo como una persona. +1 –

-3

No, no puede. imo el mejor enfoque para OOP en C es usando ADT.

62

Lo más cerca que se puede conseguir 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 Base, usted puede hacer esto:

Base *b = (Base *)d; 

Dónde d es una instancia de Derived . Entonces son un poco polimórficos. Sin embargo, tener métodos virtuales es otro reto - para hacer eso, usted necesita tener el equivalente de un puntero vtable en Base, que contiene los punteros de función a las funciones que aceptan Base como primer argumento (que usted podría nombrar this).

Por ese momento, usted puede también utilizar C++!

+4

Bueno, eso es suponiendo que un compilador de C++ esté disponible para su plataforma. –

+4

Si hay un compilador de C disponible, entonces también lo es un compilador de C++; solo use uno que produzca C como salida. –

+1

Phew ... me salvaste la vida. Normalmente codifico en Java y cuando me enfrento con el código similar al que publicaste pensé que era una composición y que estaba confundido como el infierno cuando lo lanzaron. –

3

Puede hacer lo mencionado

typedef struct 
{ 
    // base members 

} Base; 

typedef struct 
{ 
    Base base; 

    // derived members 

} Derived; 

Pero si se quiere evitar la proyección de puntero, puede utilizar punteros a un union de Base y Derived.

33

me gusta y utiliza la idea de Typesafe inheritance in 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; 
+0

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

+1

Enfoque interesante. Sin embargo, tan pronto como escribe 'leopard.base', se elimina el punto de herencia/polimorfismo. – Powerslave

+0

¿No obtiene aquí un problema de herencia de diamantes? 'leopard.base.felidae.base.animal.weight' y' leopard.base.animal.weight'? –

8

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

typedef struct Base 
{ 
    // base members 
} Base_t; 

typedef struct 
{ 
    struct Base; //anonymous struct 

    // derived members 

} Derived_t; 

De esta manera, los miembros stuct base se puede Acessed directamente, que es más agradable.

+5

El sufijo '_t' está reservado en POSIX. Haz lo que quieras, solo ten en cuenta que es probable que encuentres conflictos si escribes tu código para un sistema POSIX (por ejemplo, Linux) o si alguien finalmente quiere transferir tu código a un sistema POSIX. – L0j1k

+6

Esto realmente no funciona en la norma C (ni siquiera C11). – Chase

+0

¿Es ese estándar C o simplemente GCC? Sólo GCC, ¿verdad? – Zorgatone

7

Si desea utilizar un poco de magia gcc (que yo supongo que funcionaría con el compilador de C de Microsoft) se puede hacer algo como:


struct A 
{ 
    int member1; 
}; 

struct B 
{ 
    struct A; 
    int member2; 
} 

con gcc puede compilar esto con -fms-extensions (Permite para miembros de estructuras sin nombre como el compilador de Microsofts). Esto es similar a la solución dada por Daniel Earwicker, excepto que le permite acceder a memeber1 en una instancia de struct B. es decir, B.member1 en lugar de B.A.member1.

Probablemente este no sea el enfoque más portable y no funcionará si se usa un compilador de C++ (semántica de lenguaje diferente significa que está redeclarando/definiendo la estructura A en lugar de crear una instancia).

Sin embargo, si usted vive en el terreno de gcc/C, solo funcionará y hará exactamente lo que quiera.

+0

¿No es esta composición? –

+0

No, es herencia propia. Suponiendo que tiene una estructura de tipo struct B llamada b, b.member1 compilará y funcionará como era de esperar. La composición sería algo así como b.base.member1. GCC realiza esta magia para ti. En realidad, tiene la definición de struct B como dos enteros en este caso. – Matt

+0

¿Es esto posible solo en C, no en C++? En caso negativo, visite [This] (http://www.learncpp.com/cpp-tutorial/102-composition/). –

3

Una ligera variación de la respuesta de anon (y otros similares). Por un lado la herencia nivel profundo uno puede hacer lo siguiente:

#define BASEFIELDS    \ 
    char name[NAMESIZE];  \ 
    char sex 

typedef struct { 
    BASEFIELDS; 
} Person; 

typedef struct { 
    BASEFIELDS; 
    char job[JOBSIZE]; 
} Employee; 

typedef struct { 
    BASEFIELDS; 
    Employee *subordinate; 
} Manager; 

De esta manera las funciones que aceptan puntero a persona, aceptarán puntero al empleado o directivo (con la fundición), al igual que en otras respuestas, pero en este caso el inicialización será natural, así:

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

vs

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

creo que así es como hacen algunos proyectos populares que (por ejemplo libuv.)

ACTUALIZACIÓN: también hay algunos buenos ejemplos de conceptos similares (pero no iguales) en la implementación de eventos libsdl usando estructuras y uniones.

+1

Esto tiene consecuencias difíciles de aliasing. Por lo que yo entiendo, solo puedes redistribuir a través de la cadena de herencia mientras las estructuras estén en la memoria dinámica. (Consulte https://gustedt.wordpress.com/2016/08/17/effective-types-and-aliasing/) Sin memoria dinámica, el enfoque de unión es probablemente mejor. – PSkocik

+0

@PSkocik Obviamente se debe tener cuidado cuando se juega con cosas como esta ya que muchas cosas pueden salir mal. Sin embargo, creo que el ejemplo también funcionará para los punteros para apilar variables. Habiendo dicho eso, estoy de acuerdo en que cuando se trata de proyectar en escenarios como este, la Unión siempre es una mejor solución. – Alex

+0

No importa cómo se asignan los objetos (automático, estático, dinámico). Un ejemplo de posibles problemas es que diferentes objetos pueden tener diferentes cantidades de relleno entre los campos 'nombre' y' sexo', pero hay otras posibilidades. El problema principal es que no hay garantías de que esto funcione, por lo que este código no es confiable y, por lo tanto, inutilizable. – user694733

2

Esto funciona compilar con -fms-extensions

Diagram image

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

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

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 <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 
+0

bueno para la prueba de concepto, pero si tienes que recorrer todo este camino para lograr OOP en C, estarás mejor con C++. En mi humilde opinión, C no está diseñado para OOP y si realmente necesita ser POO puro (con un gran esfuerzo para lograrlo), está utilizando el lenguaje equivocado. Por mi parte, no me gustaría mantener ese código. – Alex

+0

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