2009-02-07 22 views
55

¿Hay alguna manera de escribir código tipo OO en el lenguaje de programación C?¿Cómo puedo simular el polimorfismo de OO en C?


Consulte también:

encontrar buscando en "[c] oo".

+0

gracias por la pista con los corchetes. No lo sabía – prinzdezibel

+3

Sí, eso necesita hacerse más reconocible o algo así. He estado tratando de enmarcar una buena sugerencia para la voz de usuario, pero no está funcionando. – dmckee

Respuesta

48

El primer compilador de C++ ("C con clases") sería realmente generar código C, por lo que es sin duda factible.

Básicamente, su clase base es una estructura; las estructuras derivadas deben incluir la estructura base en la primera posición, de modo que un puntero a la estructura "derivada" también sea un puntero válido a la estructura base.

typedef struct { 
    data member_x; 
} base; 

typedef struct { 
    struct base; 
    data member_y; 
} derived; 

void function_on_base(struct base * a); // here I can pass both pointers to derived and to base 

void function_on_derived(struct derived * b); // here I must pass a pointer to the derived class 

Las funciones pueden ser parte de la estructura como punteros de función, de modo que una sintaxis como p-> comunicación (p) se hace posible, pero todavía tiene que pasar explícitamente un puntero a la estructura de la propia función .

+6

Esto no explica cómo funcionaría la anulación de método en C. ¿Cómo se puede anular function_on_base para acceder al memeber_y derivado como se puede en llamadas polimórficas C++? –

+0

Anulación no es posible en C. –

+6

Esta respuesta es incorrecta. Pasar una 'struct derived *' a 'function_on_base' no se compilará; 'struct derived *' es un tipo diferente de 'struct base *' incluso si la dirección es correcta; sin embargo, si convierte el puntero de 'derived *' a 'base *', funcionará (pero se perderá la comprobación de tipo en tiempo de compilación y en su lugar se bloqueará en el tiempo de ejecución). @PatrickCollins Sobrescritura _es posible en C: http://pastebin.com/W5xEytbv – weberc2

47

El enfoque común es definir struct con punteros a las funciones. Esto define 'métodos' que pueden invocarse en cualquier tipo. Los subtipos establecen sus propias funciones en esta estructura común y la devuelven.

Por ejemplo, en núcleo de Linux, hay struct:

struct inode_operations { 
    int (*create) (struct inode *,struct dentry *,int, struct nameidata *); 
    struct dentry * (*lookup) (struct inode *,struct dentry *, 
           struct nameidata *); 
    ... 
}; 

Cada tipo registrada de sistema de ficheros a continuación, registra su propias funciones para create, lookup, y las funciones restantes. Resto del código se puede utilizar de inode_operations genéricos:

struct inode_operations *i_op; 
i_op -> create(...); 
+1

Eso es básicamente cómo cfront (el compilador original de C++) convirtió C++ en C que luego se compiló con pcc. Aprendí mucho sobre cómo funcionó esto al tratar con los archivos centrales de ese desastre. –

+4

Suena divertido :-) –

23

C++ no está tan lejos de C.

clases son estructuras con un puntero oculto a una tabla de punteros de función llamada Vtable. El Vtable en sí mismo es estático. Cuando los tipos apuntan a Vtables con la misma estructura pero los punteros apuntan a otra implementación, obtienes polimorfismo.

Se recomienda encapsular la lógica de llamadas en la función que toma la estructura como parámetro para evitar el desorden del código.

También debe encapsulctar la creación de instancias y la inicialización de estructuras en funciones (esto es equivalente a un constructor C++) y la eliminación (destructor en C++). Estas son buenas prácticas de todos modos.

typedef struct 
{ 
    int (*SomeFunction)(TheClass* this, int i); 
    void (*OtherFunction)(TheClass* this, char* c); 
} VTable; 

typedef struct 
{ 
    VTable* pVTable; 
    int member; 

} TheClass; 

para llamar al método:

int CallSomeFunction(TheClass* this, int i) 
{ 
    (this->pVTable->SomeFunction)(this, i); 
} 
1

Las funciones de archivo fopen, fclose, fread son ejemplos de código orientado a objetos en C en lugar de los datos privados en la clase, que trabajan en la estructura de archivos de los cuales se utiliza para encapsular los datos y las funciones C actúan como funciones de clase miembro. http://www.amazon.com/File-Structures-Object-Oriented-Approach-C/dp/0201874016

+0

El título dice ahora: 'Un enfoque orientado a objetos con C++' – prinzdezibel

+3

El libro señalado es solo C++. Nota: Tengo una copia y no la recomendaría hoy. Si tuviera una recomendación OO en C para dar hoy sería C Interfaces e implementaciones: Técnicas para la creación de software reutilizable por David Hanson (http://amzn.com/0201498413). El libro absolutamente brillante y la mayoría de los programadores harían bien en entenderlo, la mayoría de los ejemplos en él se toman de los back-ends del compilador para que el código sea ejemplar. – Harry

12

Apéndice B del artículo Open Reusable Object Models, por Ian Piumarta y Alessandro Warth de VPRI es una implementación de un modelo de objetos en GNU C, alrededor de 140 líneas de código. ¡Es una lectura fascinante!

Aquí está la versión en caché de la macro que envía mensajes a los objetos, utilizando una extensión de GNU C (expresión comunicado):

struct object; 

typedef struct object *oop; 
typedef oop *(*method_t)(oop receiver, ...); 

//... 

#define send(RCV, MSG, ARGS...) ({ \ 
    oop r = (oop)(RCV); \ 
    method_t method = _bind(r, (MSG)); \ 
    method(r, ##ARGS); \ 
}) 

En el mismo documento, echar un vistazo a la object, vtable , vtable_delegated y symbol funciones, y las funciones _bind y vtable_lookup.

¡Salud!

12

Miré a todo el mundo vigilara de respuestas y se le ocurrió esto:

#include <stdio.h> 

typedef struct 
{ 
    int (*get)(void* this); 
    void (*set)(void* this, int i); 
    int member; 

} TheClass; 

int Get(void* this) 
{ 
    TheClass* This = (TheClass*)this; 
    return This->member; 
} 

void Set(void* this, int i) 
{ 
    TheClass* This = (TheClass*)this; 
    This->member = i; 
} 

void init(TheClass* this) 
{ 
    this->get = &Get; 
    this->set = &Set; 
} 

int main(int argc, char **argv) 
{ 
    TheClass name; 
    init(&name); 
    (name.set)(&name, 10); 
    printf("%d\n", (name.get)(&name)); 
    return 0; 
} 

Espero haber respondido a algunas preguntas.

+3

Buen ejemplo. Sería incluso mejor si tuviera 2 clases "derivadas" con diferentes init/get/set. los miembros/funciones "privados" se pueden hacer con [estructuras opacas] (http://stackoverflow.com/questions/3965279/opaque-c-structs-how-should-they-be-declared). La convención de nomenclatura también es importante: 'mylib_someClass_aMethod (this)' es una buena posibilidad. –

+0

Esto se ve bien. Puse esto en un archivo c y lo compilé bien para un microcontrolador ARM. Todavía no me estoy acercando, pero me gusta dejar caer una nota como agradecimiento a la publicación de red_hax0r. – coarist

+0

¿dónde está el polimorfismo? –

0

De Wikipedia: En lenguajes de programación y la teoría de tipos, polimorfismo (de πολύς griega, polígonos, "muchos, mucho" y μορφή, morphē, "la forma, la forma") es la provisión de una interfaz única para las entidades de diferente tipos.

Así que yo diría que la única manera de implementarlo en C es mediante el uso de argumentos variados junto con algún tipo (semi) automático de gestión de información de tipo. Por ejemplo, en C++ se puede escribir (lo siento por trivialidad):

void add(int& result, int a1, int a2); 
void add(float& result, float a1, float a2); 
void add(double& result, double a1, double a2); 

En C, entre otras soluciones, lo mejor que puede hacer es algo como esto:

int int_add(int a1, int a2); 
float float_add(float a1, fload a2); 
double double_add(double a1, double a2); 

void add(int typeinfo, void* result, ...); 

entonces usted necesita:

  1. para poner en práctica el "typeinfo" con enumeraciones/macros
  2. para poner en práctica esta última función con cosas stdarg.h
  3. para decir adiós a C tipo estático comprobando

estoy casi seguro de que cualquier otra aplicación de polimorfismo debe parecerse mucho a este mismo uno. ¡Las respuestas anteriores, en cambio, parecen intentar abordar la herencia más que el polimorfismo!

+0

En C11, la nueva palabra clave [_Generic] (http://en.cppreference.com/w/c/language/generic) simplifica mucho este patrón de diseño. Lo recomiendo encarecidamente a quienes elijan este enfoque. –

0
#include <stdio.h> 

typedef struct { 
    int x; 
    int z; 
} base; 

typedef struct { 
    base; 
    int y; 
    int x; 
} derived; 

void function_on_base(base * a) // here I can pass both pointers to derived and to base 
{ 
    printf("Class base [%d]\n",a->x); 
    printf("Class base [%d]\n",a->z); 
} 
void function_on_derived(derived * b) // here I must pass a pointer to the derived class 
{ 
    printf("Class derived [%d]\n",b->y); 
    printf("Class derived [%d]\n",b->x); 
} 

int main() 
{ 
    derived d; 
    base b; 
    printf("Teste de poliformismo\n"); 

    b.x = 2; 
    d.y = 1; 
    b.z = 3; 
    d.x = 4; 
    function_on_base(&b); 
    function_on_base(&d); 
    function_on_derived(&b); 
    function_on_derived(&d); 
    return 0; 
} 

La salida fue:

Class base [3] 
Class base [1] 
Class base [4] 
Class derived [2] 
Class derived [3] 
Class derived [1] 
Class derived [4] 

por lo que funciona, es un código polimórfico.

UncleZeiv explicó al respecto al principio.

0

Para construir también la funcionalidad OO en C, puede ver las respuestas anteriores.

Pero, (como se ha preguntado en otras preguntas redirigidas a este) si quiere entender qué es el polimorfismo, por ejemplos en lenguaje C.Tal vez estoy equivocado, pero no puedo pensar en algo tan fácil de entender como la aritmética de los indicadores C. En mi opinión, puntero aritmético es inherentemente polimórfico en C. En el siguiente ejemplo, la misma función (método en OO), es decir, la adición (+), producirá un comportamiento diferente según las propiedades de las estructuras de entrada.

Ejemplo:

double a*; 
char str*; 

a=(double*)malloc(2*sizeof(double)); 
str=(char*)malloc(2*sizeof(char)); 

a=a+2; // make the pointer a, point 2*8 bytes ahead. 

str=str+2; // make the pointer str, point 2*1 bytes ahead. 

responsabilidad: soy muy nuevo en C y con muchas ganas de ser corregido y aprender de los comentarios de otros usuarios, o incluso borrar completamente esta respuesta, en caso de que sea incorrecta. Muchas gracias,