¿Cómo se escribiría código orientado a objetos en C? [closed]

10 minutos de lectura

¿Cuáles son algunas formas de escribir código orientado a objetos en C? Especialmente con respecto al polimorfismo.


Ver también esta pregunta de desbordamiento de pila Orientación a objetos en C.

  • ldeniau.web.cern.ch/ldeniau/html/oopc.html”>Objeto Programación Orientada en C por Laurent Deniau

    John

    31 de marzo de 2009 a las 17:42

  • @Camilo Martin: Yo pedí intencionalmente puede no debería. En realidad, no estoy interesado en usar OOP en C. Sin embargo, al ver soluciones OO en C, puedo aprender más sobre los límites y/o la flexibilidad de C y también sobre formas creativas de implementar y usar polimorfismo.

    – Dina

    24 de noviembre de 2010 a las 16:17

  • OO es solo un patrón. Consulta aquí, incluso se puede hacer en Archivos .bat: dirk.rave.org/chap9.txt (Cualquier patrón se puede aplicar a cualquier lenguaje de programación si está lo suficientemente interesado, creo). Sin embargo, este es un buen alimento para el pensamiento. Y probablemente se pueda aprender mucho al aplicar esos patrones que damos por sentado en idiomas que no los tienen.

    – Camilo Martín

    24 de noviembre de 2010 a las 19:47


  • GTK – ‘disculpe, GObject – es en realidad un muy buen ejemplo de OOP (algo así) en C. Entonces, para responder a @Camilo, para la interpolación de C.

    – nuevo123456

    14 de diciembre de 2010 a las 22:59


  • Es realmente impactante cómo se pudo haber cerrado esta pregunta. Es una muy buena pregunta, una revelación, como dicen los puntos recibidos. Simplemente dice cómo se impulsa SO …

    – ceso

    12 abr 2020 a las 19:07

Ya que estás hablando de polimorfismo, entonces sí, puedes, estábamos haciendo ese tipo de cosas años antes de que surgiera C++.

Básicamente usas un struct para contener tanto los datos como una lista de punteros de función para apuntar a las funciones relevantes para esos datos.

Entonces, en una clase de comunicaciones, tendría una llamada de apertura, lectura, escritura y cierre que se mantendría 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 data goes here.
} tCommClass;

tCommClass commRs232;
commRs232.open = &rs232Open;
: :
commRs232.write = &rs232Write;

tCommClass commTcp;
commTcp.open = &tcpOpen;
: :
commTcp.write = &tcpWrite;

Por supuesto, esos segmentos de código de arriba estarían en un “constructor” como rs232Init().

Cuando ‘hereda’ de esa clase, simplemente cambia los punteros para que apunten a sus propias funciones. Todos los que llamaron a esas funciones lo harían a través de los punteros de función, dándote tu polimorfismo:

int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");

Una especie de vtable manual.

Incluso podría tener clases virtuales configurando los punteros en NULL; el comportamiento sería ligeramente diferente al de C++ (un volcado del núcleo en tiempo de ejecución en lugar de un error en tiempo de compilación).

Aquí hay una pieza de código de muestra que lo demuestra. Primero la estructura de clases de nivel superior:

#include <stdio.h>

// The top-level class.

typedef struct sCommClass {
    int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;

Luego tenemos las funciones para la ‘subclase’ de TCP:

// 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;
}

Y el HTTP también:

// Function for the HTTP 'class'.

static int httpOpen (tCommClass *http, char *fspec) {
    printf ("Opening HTTP: %s\n", fspec);
    return 0;
}
static int httpInit (tCommClass *http) {
    http->open = &httpOpen;
    return 0;
}

Y finalmente un programa de prueba para mostrarlo en acción:

// Test program.

int main (void) {
    int status;
    tCommClass commTcp, commHttp;

    // Same 'base' class but initialised to different sub-classes.

    tcpInit (&commTcp);
    httpInit (&commHttp);

    // Called in exactly the same manner.

    status = (commTcp.open)(&commTcp, "bigiron.box.com:5000");
    status = (commHttp.open)(&commHttp, "http://www.microsoft.com");

    return 0;
}

Esto produce la salida:

Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com

para que pueda ver que se están llamando a las diferentes funciones, dependiendo de la subclase.

  • La encapsulación es bastante fácil, el polimorfismo es factible, pero la herencia es complicada

    – Martín Beckett

    19 de julio de 2010 a las 16:10

  • lwn.net publicó recientemente un artículo titulado Patrones de diseño orientado a objetos en el kernel sobre el tema de estructuras similares a la respuesta anterior, es decir, una estructura que contiene punteros de función, o un puntero a una estructura que tiene funciones que llevan un puntero a la estructura con los datos con los que estamos trabajando como parámetro.

    usuario107498

    5 de junio de 2011 a las 0:51


  • +1 ¡Buen ejemplo! Aunque si alguien realmente quiere seguir este camino, sería más apropiado que las estructuras de “instancia” tuvieran un campo único apuntando a su instancia de “tabla virtual”, que contiene todas las funciones virtuales para ese tipo en un solo lugar. es decir, tu tCommClass sería renombrado en tCommVTy un tCommClass struct solo tendría campos de datos y un solo tCommVT vt campo que apunta a la “única y única” mesa virtual. Llevar todos los punteros con cada instancia agrega una sobrecarga innecesaria y se parece más a cómo haría las cosas en JavaScript que en C ++, en mi humilde opinión.

    – Groo

    23 de octubre de 2013 a las 7:45


  • Entonces, esto demuestra la implementación de una sola interfaz, pero ¿qué pasa con la implementación de múltiples interfaces? ¿O la herencia múltiple?

    – weberc2

    7 julio 2014 a las 15:35

  • Weber, si desea toda la funcionalidad de C++, probablemente debería usar C++. La pregunta formulada específicamente sobre el polimorfismo, la capacidad de los objetos para tomar una “forma” diferente. Ciertamente puede hacer interfaces y herencia múltiple en C, pero es un poco de trabajo extra, y tiene que administrar la inteligencia usted mismo en lugar de usar las cosas integradas de C++.

    – pax diablo

    7 julio 2014 a las 22:05

Los espacios de nombres a menudo se hacen haciendo:

stack_push(thing *)

en vez de

stack::push(thing *)

Hacer un C estructurar en algo como un C++ clase que puedes convertir:

class stack {
     public:
        stack();
        void push(thing *);
        thing * pop();
        static int this_is_here_as_an_example_only;
     private:
        ...
};

En

struct stack {
     struct stack_type * my_type;
     // Put the stuff that you put after private: here
};
struct stack_type {
     void (* construct)(struct stack * this); // This takes uninitialized memory
     struct stack * (* operator_new)(); // This allocates a new struct, passes it to construct, and then returns it
     void (*push)(struct stack * this, thing * t); // Pushing t onto this stack
     thing * (*pop)(struct stack * this); // Pops the top thing off the stack and returns it
     int this_is_here_as_an_example_only;
}Stack = {
    .construct = stack_construct,
    .operator_new = stack_operator_new,
    .push = stack_push,
    .pop = stack_pop
};
// All of these functions are assumed to be defined somewhere else

Y hacer:

struct stack * st = Stack.operator_new(); // Make a new stack
if (!st) {
   // Do something about it
} else {
   // You can use the stack
   stack_push(st, thing0); // This is a non-virtual call
   Stack.push(st, thing1); // This is like casting *st to a Stack (which it already is) and doing the push
   st->my_type.push(st, thing2); // This is a virtual call
}

No hice el destructor ni eliminé, pero sigue el mismo patrón.

this_is_here_as_an_example_only es como una variable de clase estática, compartida entre todas las instancias de un tipo. Todos los métodos son realmente estáticos, excepto que algunos toman este *

  • Si bien los conceptos de este libro son sólidos, perderá la seguridad de los tipos.

    – diapiro

    30 de junio de 2009 a las 20:29

  • Antes de lo que conocemos como patrones de diseño, existía el patrón de diseño conocido como “orientación a objetos”; lo mismo con la recolección de basura, y otros similares. Están tan arraigados ahora que tendemos a olvidar, cuando se diseñaron por primera vez, era de la misma manera que con lo que hoy consideramos patrones de diseño.

    – Dexígeno

    27 de agosto de 2010 a las 22:46

  • Puede obtenerlo directamente desde el sitio del autor: cs.rit.edu/~ats/books/ooc.pdf otros artículos del mismo autor: cs.rit.edu/~ats/books/index.html

    – pakman

    28 de julio de 2012 a las 0:33

  • La colección adecuada (Libro + ejemplos de código fuente) está disponible en este índice de rit.edu Programación orientada a objetos con ANSI-C

    –David C. Rankin

    2 de septiembre de 2014 a las 8:59

  • ¿Este libro está revisado por pares? Hay un error tipográfico en la primera oración del primer párrafo de la primera página.

    – novios

    14/07/2015 a las 18:25

Creo que además de ser útil por derecho propio, implementar OOP en C es una excelente manera de aprender OOP y comprender su funcionamiento interno. La experiencia de muchos programadores ha demostrado que para usar una técnica de manera eficiente y segura, un programador debe comprender cómo se implementan en última instancia los conceptos subyacentes. La emulación de clases, herencia y polimorfismo en C enseña precisamente esto.

Para responder a la pregunta original, aquí hay un par de recursos que enseñan cómo hacer OOP en C:

La publicación del blog EmbeddedGurus.com “Programación basada en objetos en C” muestra cómo implementar clases y herencia única en C portátil:
http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c/

La nota de aplicación “”C+”—Programación orientada a objetos en C” muestra cómo implementar clases, herencia única y enlace tardío (polimorfismo) en C usando macros de preprocesador:
http://www.state-machine.com/resources/cplus_3.0_manual.pdfel código de ejemplo está disponible en http://www.state-machine.com/resources/cplus_3.0.zip

Lo he visto hacer. No lo recomendaría. C++ originalmente comenzó de esta manera como un preprocesador que producía código C como un paso intermedio.

Esencialmente, lo que termina haciendo es crear una tabla de despacho para todos sus métodos donde almacena sus referencias a funciones. Obtener una clase implicaría copiar esta tabla de despacho y reemplazar las entradas que desea anular, y sus nuevos “métodos” deben llamar al método original si desea invocar el método base. Eventualmente, terminas reescribiendo C++.

  • “Eventualmente, terminas reescribiendo C++” Me preguntaba si/temía que ese fuera el caso.

    – Dina

    14 de abril de 2010 a las 15:53

  • O bien, podría terminar reescribiendo el Objetivo C, lo que sería un resultado mucho más atractivo.

    – Profesor Falken

    5 de noviembre de 2010 a las 12:15

  • Existe el sabor sin clase de OOP, como en JavaScript, donde el gurú dice: “No necesitamos clases para hacer muchos objetos similares”. Pero me temo que esto no es fácil de lograr en C. Sin embargo, no estoy (todavía) en condiciones de decirlo. (¿Existe una rutina clone() para clonar una estructura?)

    – Lumi

    11 de junio de 2011 a las 21:32

  • Otro tipo inteligente, que en realidad tuvo que implementar eso y hacer esa implementación rápido (Google, motor V8) ha hecho todo lo posible para agregar clases (ocultas) a JavaScript.

    – cubuspl42

    10 de febrero de 2013 a las 21:03


  • no es glib escrito en C de manera objetiva?

    – kravemir

    6 de febrero de 2018 a las 7:42

¿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