Suponiendo que tengo que usar C (no C ++ ni compiladores orientados a objetos) y no tengo una asignación de memoria dinámica, ¿cuáles son algunas técnicas que puedo usar para implementar una clase o una buena aproximación de una clase? ¿Es siempre una buena idea aislar la "clase" en un archivo separado? Suponga que podemos preasignar la memoria asumiendo un número fijo de instancias, o incluso definiendo la referencia a cada objeto como una constante antes del tiempo de compilación. Siéntase libre de hacer suposiciones sobre qué concepto de OOP necesitaré implementar (variará) y sugerir el mejor método para cada uno.
Restricciones:
- Tengo que usar C y no una OOP porque estoy escribiendo código para un sistema incrustado, y el compilador y la base de código preexistente está en C.
- No hay asignación de memoria dinámica porque no tenemos suficiente memoria para asumir razonablemente que no se acabará si comenzamos a asignarla dinámicamente.
- Los compiladores con los que trabajamos no tienen problemas con los punteros de función
apr_
y GLib los prefijag_
para crear un espacio de nombres) y otros factores de organización sin OOP. Si va a reestructurar la aplicación de todos modos, consideraría pasar algún tiempo tratando de encontrar una estructura de procedimiento más sostenible.Respuestas:
Eso depende del conjunto de características "orientado a objetos" que desea tener. Si necesita cosas como sobrecarga y / o métodos virtuales, probablemente necesite incluir punteros de función en las estructuras:
Esto le permitiría implementar una clase, "heredando" la clase base e implementando una función adecuada:
Por supuesto, esto requiere que también implemente un constructor, que se asegura de que el puntero de la función esté configurado correctamente. Normalmente asignaría memoria dinámicamente para la instancia, pero también puede dejar que la persona que llama haga eso:
Si desea varios constructores diferentes, tendrá que "decorar" los nombres de las funciones, no puede tener más de una
rectangle_new()
función:Aquí hay un ejemplo básico que muestra el uso:
Espero que esto te dé algunas ideas, al menos. Para un marco orientado a objetos exitoso y rico en C, busque en la biblioteca GObject de glib .
También tenga en cuenta que no hay una "clase" explícita modelada anteriormente, cada objeto tiene sus propios punteros de método, que es un poco más flexible de lo que normalmente encontraría en C ++. Además, cuesta memoria. Puede alejarse de eso rellenando los punteros del método en una
class
estructura e inventando una forma para que cada instancia de objeto haga referencia a una clase.fuente
const ShapeClass *
oconst void *
como argumentos? Parecería que esto último podría ser un poco más amable con la herencia, pero puedo ver argumentos en ambos sentidos ...float (*computeArea)(const ShapeClass *shape);
decir queShapeClass
es un tipo desconocido.struct
referencias en sí, requiere una declaración antes de que se defina. Esto se explica con un ejemplo aquí en la respuesta de Lundin . La modificación del ejemplo para incluir la declaración directa debería resolver su problema;typedef struct ShapeClass ShapeClass; struct ShapeClass { float (*computeArea)(const ShapeClass *shape); };
También tuve que hacerlo una vez para hacer la tarea. Seguí este enfoque:
Un ejemplo simple sería este:
fuente
typedef struct Queue Queue;
allí.Si solo desea una clase, use una matriz de
struct
s como datos de "objetos" y páselos a las funciones "miembro". Puede usartypedef struct _whatever Whatever
antes de declararstruct _whatever
ocultar la implementación del código del cliente. No hay diferencia entre tal "objeto" y elFILE
objeto de biblioteca estándar C.Si desea más de una clase con herencia y funciones virtuales, es común tener punteros a las funciones como miembros de la estructura, o un puntero compartido a una tabla de funciones virtuales. La biblioteca GObject usa tanto este como el truco typedef, y es ampliamente utilizado.
También hay un libro sobre técnicas de este disponible en línea - Programación Orientada a Objetos con la norma ANSI C .
fuente
puedes echar un vistazo a GOBject. Es una biblioteca de sistema operativo que le ofrece una forma detallada de hacer un objeto.
http://library.gnome.org/devel/gobject/stable/
fuente
C Interfaces e implementaciones: técnicas para crear software reutilizable , David R. Hanson
Este libro hace un excelente trabajo al cubrir su pregunta. Está en la serie Addison Wesley Professional Computing.
El paradigma básico es algo como esto:
fuente
Daré un ejemplo simple de cómo se debe hacer OOP en C. Me doy cuenta de que esta película es de 2009, pero me gustaría agregar esto de todos modos.
El concepto básico implica colocar la 'clase heredada' en la parte superior de la estructura. De esta manera, el acceso a los primeros 4 bytes en la estructura también accede a los primeros 4 bytes en la 'clase heredada' (Asumiendo optimizaciones no locas). Ahora, cuando el puntero de la estructura se lanza a la 'clase heredada', la 'clase heredada' puede acceder a los 'valores heredados' de la misma manera que accedería a sus miembros normalmente.
Esto y algunas convenciones de nomenclatura para constructores, destructores, funciones de asignación y reparto (recomiendo init, clean, new, free) lo ayudarán en gran medida.
En cuanto a las funciones virtuales, use punteros de función en la estructura, posiblemente con Class_func (...); envoltorio también. En cuanto a las plantillas (simples), agregue un parámetro size_t para determinar el tamaño, requiera un puntero nulo * o requiera un tipo 'clase' con solo la funcionalidad que le interesa. (por ejemplo, int GetUUID (Object * self); GetUUID (& p);)
fuente
Use a
struct
para simular los miembros de datos de una clase. En términos del alcance del método, puede simular métodos privados colocando los prototipos de funciones privadas en el archivo .c y las funciones públicas en el archivo .h.fuente
fuente
En su caso, la buena aproximación de la clase podría ser un ADT . Pero aún así no será lo mismo.
fuente
Mi estrategia es:
¿Alguien ve algún problema, agujeros, posibles dificultades o beneficios / inconvenientes ocultos para cualquiera de las variaciones de este enfoque? Si estoy reinventando un método de diseño (y supongo que debo hacerlo), ¿puede indicarme el nombre?
fuente
También vea esta respuesta y esta
Es posible. Siempre parece una buena idea en ese momento, pero luego se convierte en una pesadilla de mantenimiento. Su código se llena de fragmentos de código que unen todo. Un nuevo programador tendrá muchos problemas para leer y comprender el código si usa punteros de función, ya que no será obvio qué funciones se llaman.
La ocultación de datos con funciones get / set es fácil de implementar en C, pero se detiene allí. He visto múltiples intentos de esto en el entorno embebido y al final siempre es un problema de mantenimiento.
Como todos ustedes tienen problemas de mantenimiento, me mantendría al margen.
fuente
Mi enfoque sería mover las
struct
y todas las funciones asociadas principalmente a un archivo (s) de origen separado para que pueda usarse "de forma portátil".Dependiendo de su compilador, es posible que pueda incluir funciones en el
struct
, pero esa es una extensión muy específica del compilador, y no tiene nada que ver con la última versión del estándar que usé habitualmente :)fuente
El primer compilador de C ++ en realidad fue un preprocesador que tradujo el código de C ++ a C.
Por lo tanto, es muy posible tener clases en C. Puede intentar desenterrar un antiguo preprocesador de C ++ y ver qué tipo de soluciones crea.
fuente
cfront
; se topó con problemas cuando se agregaron excepciones a C ++; el manejo de excepciones no es trivial.GTK está construido completamente en C y utiliza muchos conceptos OOP. He leído el código fuente de GTK y es bastante impresionante, y definitivamente más fácil de leer. El concepto básico es que cada "clase" es simplemente una estructura y funciones estáticas asociadas. Todas las funciones estáticas aceptan la estructura de "instancia" como parámetro, hacen lo que sea necesario y devuelven resultados si es necesario. Por ejemplo, puede tener una función "GetPosition (CircleStruct obj)". La función simplemente cavaría a través de la estructura, extraería los números de posición, probablemente construiría un nuevo objeto PositionStruct, pegaría las x e y en la nueva PositionStruct y lo devolvería. GTK incluso implementa la herencia de esta manera al incorporar estructuras dentro de las estructuras. muy inteligente.
fuente
¿Quieres métodos virtuales?
Si no es así, simplemente define un conjunto de punteros de función en la estructura misma. Si asigna todos los punteros de función a funciones C estándar, podrá invocar funciones desde C en una sintaxis muy similar a como lo haría en C ++.
Si quieres tener métodos virtuales, se vuelve más complicado. Básicamente, necesitará implementar su propia VTable en cada estructura y asignar punteros de función a la VTable dependiendo de la función que se llame. Entonces necesitaría un conjunto de punteros de función en la estructura misma que a su vez llama al puntero de función en la VTable. Esto es, esencialmente, lo que hace C ++.
Sin embargo, TBH ... si quieres lo último, entonces probablemente sea mejor solo encontrar un compilador de C ++ que puedas usar y volver a compilar el proyecto. Nunca he entendido que la obsesión con C ++ no sea utilizable en embebido. Lo he usado muchas veces y funciona es rápido y no tiene problemas de memoria. Seguro que tienes que ser un poco más cuidadoso con lo que haces, pero en realidad no es tan complicado.
fuente
C no es un lenguaje OOP, como lo señala correctamente, por lo que no hay una forma integrada de escribir una clase verdadera. Su mejor opción es mirar estructuras y punteros de función , estos le permitirán construir una aproximación de una clase. Sin embargo, como C es de procedimiento, es posible que desee considerar escribir más código similar a C (es decir, sin tratar de usar clases).
Además, si puede usar C, puede usar probablemente C ++ y obtener clases.
fuente