Los miembros privados en las estructuras de C

18 de mayo 2008 Publicado en C / C + +

Esta semana, aquí en el trabajo tenía más prueba de que el paradigma de programación es algo completamente independiente del lenguaje, es decir, no es porque usted es la programación en C + +, se compila con g + + que su código será orientado a objetos, tan poco, si se programa en ANSI C el código debe ser estructurada o no podrá programación orientada a objetos.

Que estoy leyendo, Orientación a Objetos en ANSI C?

Sí y no!

Cosas como herencia, polimorfismo y la sobrecarga es más difícil 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 "objeto" orientada, de manera que crear, manipular para destruir los elementos que se comportan de manera muy similar a 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 inferior, también fue escrito en C (y escrito muy bien, decir por la forma) con estas técnicas, simulando una orientación a objetos. Una de estas técnicas, me sorprendió con el uso de una de esas notas al pie de los libros de C.

Al compilar código en 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 las otras unidades. Así, es posible "ocultar" ciertos símbolos dentro de su unidad de compilación, haciéndolos inaccesibles para el mundo exterior. Tenemos con este paquete.

Una unidad de compilación es el conjunto de archivos que el código una vez procesados y pre-compilado genera un solo objeto. Básicamente (pero no exactamente), podemos tomar como c cada unidad de compilación aplicación archivo de código fuente (*., *. cpp. Etc). más detalles sobre la elaboración de medidas de C y C + + se pueden encontrar en el blog Caloni .

Con esta información, podemos crear una estructura ANSI C en el que sus miembros internos son "privados". 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 definición de elementos de C:

Declaración o manifiesto: el compilador presenta un identificador sin decir mucho sobre su significado, es decir, le dice al compilador que el identificador XXX existe, pero poco se sabe sobre lo que él representa.
Por ejemplo:

  a ; extern int a;
 void ) ; bla (void);
 st_data estructura; 

Definición o aplicación: Le dice al compilador que el identificador es determinado, tales como la cantidad de memoria debe asignarse a la misma y cuál es la dirección de memoria donde podemos encontrar, entre otras cosas.
Por ejemplo:

  int a;
 void ) { /* Do anything. bla (void) (/ * Hacer cualquier cosa.  * /)
 /* Some members. st_data struct (/ * Añadir miembros.  * /) 

Cuando hablamos de estructuras y tipos, sin establecer el compilador no puede asignar memoria para ellos porque no se sabe nada acerca de cuánto espacio de una variable de ese tipo de necesidad. Por otro lado, a veces sin la declaración, el vinculador no puede saber lo que ese símbolo existe.

Cuando declaramos una estructura en un encabezado, por lo general también definir 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 fuente de la declaración como la definición de esta estructura, por lo que los miembros de todo nuestro público a esta unidad de compilación. Esto nos permite acceso a sus miembros directamente.

Si separamos la declaración de la definición, único símbolo que representa la estructura disponible, pero no sus miembros. Por lo tanto, cualquier intento de acceso directo a un miembro, se generará un error de compilación. Un efecto secundario interesante es que a medida que el compilador no sabe nada sobre el tamaño de la estructura, no puede directamente establecer una variable de tipo de estructura, sólo un puntero a él, ya que los punteros tienen todos el mismo tamaño y el compilador sólo necesita el nombre del símbolo escribir para crear el puntero.

A continuación, encabezado mytype.h así:

  # Ifndef MY_TYPE_H
 # Definir MY_TYPE_H

 / * El typedef no es sólo seguir repitiendo la palabra struct.  * /
 / * La declaración es solamente las palabras: * /
 / * * Struct _mytype /
 typedef struct my_type _mytype;

 my_type ** ) ; create_my_type vacío (my_type **);
 my_type ** ) ; destroy_my_type vacío (my_type **);

 my_type * , int ) ; set_data void (* my_type, int);
 my_type * ) ; get_data int (* my_type);
 my_type * , const char * ) ; set_text void (* my_type, const char *);
 get_text ( my_type * ) ; get_text char * (* my_type);

 # Endif 

La aplicación mytype.c:

  # Include "mytype.h"

 # <stdio.h>
 # Incluir <stdlib.h>
 # Incluir <string.h>

 /* Aqui fica a definição da estrtura. _mytype struct (/ * Esta es la definición de estrtura.  * /
     /* Somente depois disso é que o com-  */ int fecha; / * Sólo después de que, con-* /
     21 ] ; /* pilador vai saber como alocá-la. texto int [21] / * pilador sabrá que le correspondía.  * /
 /* Tente um sizeof(my_type) no main. ), / * Intente un sizeof (my_type) en el principal.  * /

 my_type ** my_ptr ) { /* some code... create_my_type void (my_ptr my_type **) (/ * Código ...  * /)
 my_type ** my_ptr ) { /* some code... destroy_my_type void (my_ptr my_type **) (/ * Código ...  * /)
 my_type * my_ptr , int d ) { /* some code... set_data vacío (* my_type my_ptr, int d) (/ * Código ...  * /)
 my_type * my_ptr ) { /* some code... get_data int (* my_ptr my_type) (/ * Código ...  * /)
 my_type * my_ptr , const char * text ) { /* some code... set_text void (* my_ptr my_type, char * const texto) (/ * Código ...  * /)
 get_text ( my_type * my_ptr ) { /* some code... char * get_text (* my_ptr my_type) (/ * Código ...  * /) 

En el ejemplo de código fuente completo se puede encontrar aquí .

Puesto que no se puede crear directamente las variables de este tipo, debemos definir un "constructor" y un destructor ".

Los intentos de acceso directo a los miembros de generar 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 mytype.h main.c mytype.c
 main.c: En la función 'main':
 main.c: 22: error: puntero de eliminación de referencias al tipo incompleto
 private-struct-members$ user @ host: ~ / privado estructura miembros $ 

Así, con un poco de creatividad y tomando los conceptos del lenguaje como ambos paradigmas, puede implementar códigos realmente interesante. En este ejemplo tonto no podría clara la utilidad de forzar una emulación de la encapsulación y el uso de constructor / destructor en C, pero en los sistemas donde las circunstancias no permiten que un C + +, oa la complejidad tiende a alcanzar niveles críticos, estas técnicas son muestra de gran valor. En nuestro caso, esta técnica se permitan específicamente un programador experimentado, quien se perdió la totalidad del proyecto, descubrió que su intento de acceder directamente a un miembro de una estructura, se contextualmente inapropiado. Sin ella, una lógica de errores Kabul sólo aparecen en tiempo de ejecución, haciendo 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/
Linux Kernel Libro Desarrollo

Comentarios

    blog alimentado por los comentarios Disqus