resueltos - Programación orientada a objetos en C
programacion orientada a objetos c++ libro pdf (13)
Sin embargo, ¿cómo emularías la encapsulación y la herencia?
En realidad, la encapsulación es la parte más fácil. La encapsulación es una filosofía de diseño , no tiene nada que ver con el lenguaje ni con la forma en que piensas sobre los problemas.
Por ejemplo, la api de Windows FILE está completamente encapsulada. Cuando abre un archivo, obtiene un objeto opaco que contiene toda la información de estado para el archivo ''objeto''. Usted devuelve este identificador a cada uno de los archivos io apis. La encapsulación es en realidad mucho mejor que C ++ porque no hay un archivo de encabezado público que las personas puedan ver y ver los nombres de sus variables privadas.
La herencia es más difícil, pero no es necesaria en absoluto para que su código esté orientado a objetos. De alguna manera, la agregación es mejor que la herencia de todos modos, y la agregación es igual de fácil en C que en C ++. ver this por ejemplo.
En respuesta a Neil, ver Wikipedia para una explicación de por qué la herencia no es necesaria para el polimorfismo.
Nosotros, los veteranos, escribimos código orientado a objetos años antes de que los compiladores de C ++ estuvieran disponibles, es una forma de pensar, no un conjunto de herramientas.
Posibles duplicados:
¿Puedes escribir código orientado a objetos en C?
Patrón orientado a objetos en C?
Recuerdo haber leído hace un tiempo sobre alguien (creo que era Linus Torvalds) hablando sobre cómo C ++ es un lenguaje horrible y cómo se puede escribir programas orientados a objetos con C. Al tener tiempo para reflexionar, realmente no veo cómo todos los conceptos orientados a objetos se transfieren a C. Algunas cosas son bastante obvias. Por ejemplo:
- Para emular las funciones de miembro, puede poner punteros de función en estructuras.
- Para emular el polimorfismo, puede escribir una función que tome una cantidad variable de argumentos y hacer algo de vudú según, por ejemplo, el
sizeof
del parámetro (s)
Sin embargo, ¿cómo emularías la encapsulación y la herencia?
Supongo que la encapsulación podría ser emulada por tener una estructura anidada que almacenaba miembros privados. Sería bastante fácil de usar, pero quizás podría llamarse PRIVATE
o algo igualmente obvio para indicar que no está destinado a ser utilizado desde fuera de la estructura. ¿Y qué hay de la herencia?
¿Has leído la "biblia" sobre el tema? Ver Object Oriented C ...
Buen artículo y discusión sobre Objective-C aquí:
http://cocoawithlove.com/2009/10/objective-c-niche-why-it-survives-in.html
Definitivamente mira a Objective-C.
typedef struct objc_object {
Class isa;
} *id;
typedef struct objc_class {
struct objc_class *isa;
struct objc_class *super_class
const char *name;
long version;
long info
long instance_size;
struct objc_ivar_list *ivars;
struct objc_method_list **methodLists;
struct objc_cache *cache;
struct objc_protocol_list *protocols;
} *Class;
Como puede ver, la información de herencia, junto con otros detalles, se mantiene en una estructura de clase (convenientemente la clase también se puede tratar como un objeto).
Objective-C sufre de la misma manera que C ++ con encapsulamiento en el que necesita declarar públicamente sus variables. Straight C es mucho más flexible, ya que solo puede devolver punteros vacíos a los que solo su módulo tiene acceso interno, por lo que, en ese aspecto, la encapsulación es mucho mejor.
Una vez escribí un programa básico de pintura C estilo OO como parte de un curso de gráficos. No llegué tan lejos como la declaración de clase, simplemente usé un puntero vtable como el primer elemento de la estructura e implementé la herencia codificada a mano. Lo bueno de jugar con los vtables a un nivel tan bajo es que puede cambiar el comportamiento de la clase en tiempo de ejecución cambiando algunos punteros o cambiando dinámicamente la clase de los objetos. Fue bastante fácil crear todo tipo de objetos híbridos, herencia múltiple falsa, etc.
Desde una altitud un poco más alta y considerando que el problema es más abierto de lo que podría sugerir la corriente principal OOP, la Programación Orientada a Objetos significa pensar en objetos como datos con funciones asociadas. No necesariamente significa que una función tiene que estar físicamente unida a un objeto como lo es en los lenguajes populares que soportan el paradigma de OOP, por ejemplo en C ++:
struct T
{
int data;
int get_data() const { return data; }
};
Sugeriría echar un vistazo más de cerca a GTK + Object y Type System . Es un brillante ejemplo de OOP realizado en lenguaje de programación C:
GTK + implementa su propio sistema de objetos personalizados, que ofrece funciones estándar orientadas a objetos, como la herencia y la función virtual
La asociación también puede ser contractual y convencional.
Con respecto a las técnicas de encapsulación y ocultación de datos, popular y simple puede ser un puntero opaco (o tipo de datos opaco): puede pasarlo, pero para cargar o almacenar cualquier información, debe llamar a una función asociada que sepa cómo hablar con el objeto escondido detrás de ese puntero opaco.
Otro, similar pero diferente es el tipo de datos de sombra : consulte este enlace donde Jon Jagger explica de forma excelente esta técnica no tan conocida.
Eche un vistazo a la forma en que la capa de VFS funciona en el kernel de Linux para un ejemplo de un patrón de herencia. Las operaciones de archivos para los diversos sistemas de archivos "heredan" un conjunto de funciones genéricas de operaciones de archivos (por ejemplo, generic_file_aio_read()
, generic_file_llseek()
...), pero pueden anularlas con sus propias implementaciones (por ejemplo, ntfs_file_aio_write()
).
El framework CoreFoundation basado en C de Apple se escribió en realidad para que sus "objetos" se pudieran duplicar como objetos en Objective-C, un lenguaje de OO real. Un subconjunto bastante grande del framework es de código abierto en el sitio de Apple como CF-Lite . Puede ser un estudio de caso útil en un marco de nivel de SO importante hecho de esta manera.
Es posible que desee ver Objective-C, eso es más o menos lo que hace. Es solo un front-end que compila el código OO de Objective-C a C.
Para un gran ejemplo de programación orientada a objetos en C, mira la fuente de POV-Ray de hace varios años: la versión 3.1g es particularmente buena. Los "objetos" eran struct con indicadores de función, por supuesto. Las macros se usaron para proporcionar los métodos centrales y los datos para un objeto abstracto, y las clases derivadas fueron estructuras que comenzaron con esa macro. Sin embargo, no hubo ningún intento de tratar con lo privado / público. Las cosas que se veían estaban en archivos .h y los detalles de implementación estaban en archivos .c, en su mayoría, excepto por muchas excepciones.
Hubo algunos trucos ingeniosos que no veo cómo podrían trasladarse a C ++, como la conversión de una clase a una diferente pero similar sobre la marcha con la reasignación de punteros a funciones. Simple para los lenguajes dinámicos de hoy. Olvidé los detalles; Creo que podría haber sido intersección CSG y objetos de unión.
Puede implementar polimorfismo con funciones regulares y tablas virtuales (vtables). Este es un sistema bastante ingenioso que inventé (basado en C ++) para un ejercicio de programación:
Los constructores asignan memoria y luego llaman a la función init de la clase donde se inicializa la memoria. Cada función init también debe contener una estructura vtable estática que contenga los punteros de función virtual (NULL para pura virtual). Las funciones de inicio de clase derivadas llaman a la función init de la superclase antes de hacer cualquier otra cosa.
Se puede crear una muy buena API implementando los envoltorios de funciones virtuales (que no deben confundirse con las funciones señaladas por los vtables) de la siguiente manera (agregue líneas static inline
en static inline
delante de él, si hace esto en el encabezado):
int playerGuess(Player* this) { return this->vtable->guess(this); }
La herencia individual se puede hacer abusando del diseño binario de una estructura:
Tenga en cuenta que la herencia múltiple es más desordenada, ya que a menudo necesita ajustar el valor del puntero al convertir entre los tipos de la jerarquía.
También se pueden agregar otros datos específicos de tipo a las tablas virtuales. Los ejemplos incluyen información del tipo de tiempo de ejecución (por ejemplo, nombre de tipo como una cadena), vincular a superclase vtable y la cadena de destructor. Probablemente desee destructores virtuales donde el destructor de clases derivado degrada el objeto a su superclase y luego llama recursivamente al destructor de ese y así sucesivamente, hasta que se alcanza el destructor de la clase base y eso finalmente libera la estructura.
La encapsulación se realizó definiendo las estructuras en player_protected.h e implementando las funciones (apuntadas por el vtable) en player_protected.c, y de forma similar para las clases derivadas, pero esto es bastante torpe y degrada el rendimiento (ya que los wrappers virtuales no se pueden poner a encabezados), así que recomendaría no hacerlo.
Un poco de historia interesante. Cfront , el código C de la implementación original de C ++ y luego requiere un compilador de C para construir el código final. Entonces, cualquier cosa que pueda expresarse en C ++ podría escribirse como C.
Una forma de manejar la herencia es tener estructuras anidadas:
struct base
{
...
};
void method_of_base(base *b, ...);
struct child
{
struct base base_elements;
...
};
A continuación, puede hacer llamadas como esta:
struct child c;
method_of_base(&c.b, ...);
las bibliotecas gtk y glib usan macros para lanzar objetos a varios tipos.
add_widget (GTK_WIDGET (myButton));
No puedo decir cómo se hace, pero puede leer su fuente para descubrir exactamente cómo se hace.