Los miembros privados en las estructuras de C
Esta semana, aquí en el trabajo tenía una prueba más de que el paradigma de programación es totalmente lenguaje algo independiente, es decir, no es porque usted está programando en C + +, compilado con g + + de que su código será orientado a objetos tan poco, si se programa en su código ANSI C debe ser estructurada o no será capaz de programación orientada a objetos.
Estoy leyendo la correcta orientación, de objetos en ANSI C?
Sí y no!
Cosas como la herencia, el polimorfismo y la sobrecarga son complicados de hacer / emular en C, pero se puede programar con un estilo que se comporta de manera similar a la orientación a objetos. El libdfb está escrito en C, pero "orientado a objetos", por lo que crear, manipular destruye los elementos que se comportan igual que los objetos.
Aquí en el trabajo, tenemos un sistema de abstracción de hardware en C que se conecta con una interfaz gráfica de usuario en C + +. La capa más baja, también fue escrito en C (y muy bien escrito, por ejemplo, por cierto) con estas técnicas, la simulación de una orientación a objetos. Una de estas técnicas, me llamó la atención de usar una de esas notas al pie de los libros de C.
Al compilar el código C, cada símbolo sólo es visible dentro de la unidad de compilación en la que fue declarada, a menos que se volvió a declarar como extern en otras unidades. Por lo tanto, es posible "ocultar" algunos de los símbolos dentro de su unidad de compilación, haciéndolos inaccesibles para el resto del mundo. Tenemos con este paquete.
La unidad de compilación es el conjunto de archivos que, una vez pre-procesado y generar un único código objeto compilado. Básicamente (pero no exactamente), podemos tomar para cada archivo de compilación de la unidad de ejecución del código fuente (*. C, *. Cpp. Etc.). Para más detalles sobre los pasos de compilación de C y C + + se puede encontrar en el blog de Caloni .
Con esta información, podemos crear una estructura en ANSI C en el que sus miembros son internas "privado". La magia está en la definición de los estados trampa dentro de la unidad de compilación y crear métodos de acceso para estos miembros. Para ello se utilizan las notas que nos muestran la diferencia entre la declaración y la definición de los elementos en C:
Declaración o manifiesto: el compilador presenta un identificador sin decir mucho acerca de su significado, es decir, le indica al compilador que el XXX identificador existe, pero se sabe poco sobre lo que representa.
Por ejemplo,
a ; extern int a; void ) ; bla vacío (void); estructura st_data;
Definición e implementación: Le dice al compilador que representa el identificador dado, tales como la cantidad de memoria debe asignarse a la misma y lo que es la dirección de memoria donde podemos encontrar, entre otras cosas.
Por ejemplo,
int a; void ) { /* Do anything. vacío, bla (void) {/ * hacer nada. * /} /* Some members. st_data struct {/ * Añadir miembros. * /}
Cuando hablamos de estructuras y tipos, sin definir el compilador no puede asignar memoria para ellos porque no se sabe nada acerca de cuánto espacio se necesita una variable de ese tipo. Por otro lado, a veces sin la declaración, el enlazador no puede saber lo que el símbolo existe.
Cuando declaramos una estructura en un encabezado, por lo general se han establecido también sus miembros allí y cada vez que añadimos esta cabecera a una fuente de la nuestra, hemos incluido en la unidad de compilación que tanto la declaración de origen como la definición de esta estructura, por lo que los miembros del público estutura a esta unidad de compilación. Esto nos permite acceder directamente a sus miembros.
Si separamos la declaración de la definición, sólo el símbolo que representa la estructura está disponible, pero no a sus miembros. Por lo tanto, cualquier intento de acceso directo a un miembro, va a generar un error de compilación. Un efecto secundario interesante es que como el compilador no sabe nada sobre el tamaño de la estructura, usted no directamente puede definir una variable de estructura tipo, sólo un puntero a él, porque todos los punteros tienen el mismo tamaño y el compilador sólo necesita el nombre del símbolo escriba para crear el puntero.
A continuación, encabezado mytype.h de esta manera:
# Ifndef MY_TYPE_H # Definir MY_TYPE_H / * El typedef no es sólo para mantener la repetición de la palabra struct. * / / * La declaración es sólo las palabras: * / / * Struct _MYTYPE * / typedef struct _MYTYPE my_type; my_type ** ) ; void create_my_type (my_type **); my_type ** ) ; void destroy_my_type (my_type **); my_type * , int ) ; void set_data (my_type *, int); my_type * ) ; int get_data (my_type *); my_type * , const char * ) ; void set_text (my_type *, const char *); get_text ( my_type * ) ; get_text char * (* my_type); # Endif
El mytype.c aplicación:
_mytype { /* Aqui fica a definição da estrtura. # Include "mytype.h" # include # include # include <string.h> _MYTYPE struct {/ * Aquí está la definición de estrtura. data ; /* Somente depois disso é que o com- */ int text [ 21 ] ; /* pilador vai saber como alocá-la. * / Datos Int / * Sólo después de que, con-* / int texto [21] / * pilador sabrá cómo asignarlo. ; /* Tente um sizeof(my_type) no main. * /} / * Pruebe un sizeof (my_type) en el principal. create_my_type ( my_type ** my_ptr ) { /* some code... * / Void create_my_type (my_type my_ptr **) {/ * Código ... void destroy_my_type ( my_type ** my_ptr ) { /* some code... * /} Void destroy_my_type (my_type my_ptr **) {/ * Código ... void set_data ( my_type * my_ptr , int d ) { /* some code... * /} Void set_data (my_type my_ptr *, int d) {/ * Código ... int get_data ( my_type * my_ptr ) { /* some code... * /} Int get_data (my_type my_ptr *) {/ * Código ... void set_text ( my_type * my_ptr , const char * text ) { /* some code... * /} Void set_text (my_type my_ptr *, const char * texto) {/ * Código ... char * get_text ( my_type * my_ptr ) { /* some code... * /} Char * get_text (my_type my_ptr *) {/ * Código ... * /}
El ejemplo de código fuente completo se puede encontrar aquí .
Puesto que no se puede crear estas variables, es necesario definir un "constructor" y un "destructor".
Los intentos de acceso directo a los miembros de la generación de error de compilación:
private-struct-members$ gcc -o teste mytype.h mytype.c main.c user @ host: ~ / privado-estructura miembros $ gcc-o prueba de main.c mytype.h mytype.c main.c: En la función 'main': main.c: 22: error: puntero a la cancelación de referencia del tipo de dato incompleto private-struct-members$ user @ host: ~ / privado-struct-miembros $
Así, con un poco de creatividad y tomando los conceptos de ambos paradigmas del lenguaje, es posible poner en práctica los códigos muy interesantes. En este ejemplo tonto, no puede borrar la utilidad de obligar a un paquete de emulación o el uso del constructor / destructor de C, pero en sistemas en los que las circunstancias no permiten que un C + +, o la complejidad tiende a alcanzar niveles críticos, estas técnicas son muestra de gran valor. En nuestro caso, esta técnica específicamente permitido un programador experimentado, que no era parte de todo el proyecto, descubrió que su intento de acceso directo a un miembro de una estructura, era contextualmente inapropiados. Sin ella, una lógica de errores cabuloso que aparecen sólo en tiempo de ejecución, por lo que el software probablemente explotará en la cara del cliente.
Enlaces útiles (o no ...):
http://www.directfb.org
http://www.caloni.com.br
http://www.numaboa.com.br/informatica/c/
Libro para el Desarrollo del Kernel de Linux
Comentarios
- Diego V. Kersting
- blabos
- blabos
- blabos
- Rodrigo Martins (la bruja)
- blabos
- Carcara

