Estoy diseñando un programa en C que manipula figuras geométricas y sería muy conveniente si cualquier tipo de figura pudiera ser manipulada por las mismas primitivas.Polimorfismo en C
¿Cómo puedo hacer esto en C?
Estoy diseñando un programa en C que manipula figuras geométricas y sería muy conveniente si cualquier tipo de figura pudiera ser manipulada por las mismas primitivas.Polimorfismo en C
¿Cómo puedo hacer esto en C?
Generalmente lo hace con punteros a funciones. En otras palabras, estructuras simples que contienen los datos y punteros a funciones que manipulan esa información. Estábamos haciendo ese tipo de cosas años antes de que Bjarne S apareciera en la escena.
Así, por ejemplo, en una clase de comunicaciones, que tendría un proceso abierto, leer, escribir y pelos que se mantiene como cuatro punteros de función en la estructura, junto con los datos de un objeto, algo así como:
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 the data for the object goes here.
} tCommsClass;
tCommsClass commRs232;
commRs232.open = &rs232Open;
: :
commRs232.write = &rs232Write;
tCommsClass commTcp;
commTcp.open = &tcpOpen;
: :
commTcp.write = &tcpWrite;
La inicialización de esos punteros a funciones estaría en realidad en un "constructor" como rs232Init(tCommClass*)
, que sería responsable de establecer el estado predeterminado de ese objeto particular para que coincida con una clase específica.
Cuando 'hereda' de esa clase, simplemente cambie los punteros para señalar sus propias funciones. Cada uno que llama esas funciones lo haría a través de los punteros de función, que le da su polimorfismo:
int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");
Algo así como una configuración manual vtable, en lenguaje C++.
Incluso podría tener clases virtuales al establecer los punteros en NULL -el comportamiento sería ligeramente diferente a C++ en la medida en que probablemente obtendría un volcado del núcleo en tiempo de ejecución en lugar de un error en tiempo de compilación.
Aquí es una pieza de código de ejemplo que muestra que:
#include <stdio.h>
// The top-level class.
typedef struct _tCommClass {
int (*open)(struct _tCommClass *self, char *fspec);
} tCommClass;
// 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;
}
// Function for the HTML class.
static int htmlOpen (tCommClass *html, char *fspec) {
printf ("Opening HTML: %s\n", fspec);
return 0;
}
static int htmlInit (tCommClass *html) {
html->open = &htmlOpen;
return 0;
}
// Test program.
int main (void) {
int status;
tCommClass commTcp, commHtml;
// Same base class but initialized to different sub-classes.
tcpInit (&commTcp);
htmlInit (&commHtml);
// Called in exactly the same manner.
status = (commTcp.open)(&commTcp, "bigiron.box.com:5000");
status = (commHtml.open)(&commHtml, "http://www.microsoft.com");
return 0;
}
Esto produce la salida:
Opening TCP: bigiron.box.com:5000
Opening HTML: http://www.microsoft.com
Así se puede ver que las diferentes funciones son ser llamado, dependiendo de la subclase.
Ooh, muy inteligente. Me gusta eso. – Robert
@Robert, me gustaría reclamarle crédito, pero, en palabras de un científico inmortal, "la razón que he visto hasta ahora es porque he estado sobre los hombros de gigantes". – paxdiablo
¡Gracias! Esa estrategia es genial. Voy a esperar un par de días antes de marcar esto como la respuesta para ver si alguien propone una solución más agradable. –
La gente ha hecho cosas tontas con varios tipos de struct
s y que confían en el relleno predecible - por ejemplo, se puede definir un struct
con un subconjunto particular de otra struct
y por lo general van a trabajar. Véase más abajo (código robado de Wikipedia):
struct ifoo_version_42 {
long x, y, z;
char *name;
long a, b, c;
};
struct ifoo_old_stub {
long x, y;
};
void operate_on_ifoo(struct ifoo_version_42 *);
struct ifoo_old_stub s;
...
operate_on_ifoo(&s);
En este ejemplo, el ifoo_old_stub
podría considerarse una superclase. Como probablemente pueda averiguar, esto se basa en el hecho de que el mismo compilador rellenará las dos estructuras de forma equivalente, y tratar de acceder al x
y y
de una versión 42 funcionará incluso si pasa un talón. Esto debería funcionar al revés también. Pero AFAIK no funciona necesariamente en todos los compiladores, así que tenga cuidado si desea enviar una estructura de este formato a través de la red, o guardarla en un archivo, o llamar a una función de biblioteca con una.
Hay una razón por la cual el polimorfismo en C++ es bastante complicado de implementar ...(tablas, etc.)
Sobrecarga! = polimorfismo ni es un requisito previo. En el sentido de polimorfismo a través de una jerarquía de herencia, las diferentes implementaciones de una función polimórfica tienen argumentos de los mismos tipos, no diferentes, como funciones sobrecargadas. –
No debe evitarse. El estándar C garantiza la capacidad de convertir una 'struct *' en un puntero a su primer elemento, por lo que si usted garantiza que todos sus "objetos" contienen el mismo objeto raíz (en la línea de herencia) siempre puede lanzarlos al mismo objeto raíz –
Sin embargo, puede crear una función de agregar que tome 3 punteros, uno de los cuales es una función de suma. Esa función de adición sería completamente genérica (aunque en este caso, completamente redundante, ya que solo llamaría a la función de agregar que se le pasó). –
Estoy asombrado, nadie ha mencionado glib, gtk y el sistema GObject. Entonces, en lugar de hornear todavía, otra-oo-capa-sobre-C. ¿Por qué no usar algo que ha sido probado para funcionar?
Saludos "polimorfismo en C" Friedrich
sí, ¿por qué no mencionar COM mientras lo hace? –
Sí COM también sería un ejemplo. – Friedrich
.. es que algo así como "La tolerancia en el régimen nazi"? –
http://www.planetpdf.com/codecuts/pdfs/ooc.pdf –
Lo sé, lo sé. Fue una broma, yeesh. Tal vez no sea una buena, pero aún así, una broma, sin embargo = D –