Uniones
§1 Sinopsis
Las uniones C++ son un tipo especial de clase; un tipo de variable con cierta similitud con las estructuras ( 4.5). Pueden albergar diferentes tipos de datos, pero solo uno, de entre todos los posibles, al mismo tiempo. Se dice que solo uno puede estar "activo" en cada momento, y se corresponden con los registros de tipo variable de Pascal y Modula-2. Como el lector estará suponiendo, el tamaño de una unión es el del mayor elemento que puede albergar. El valor exacto depende de la implementación y de las alineaciones internas ( 4.5.9a).
Como se verá a continuación, desde el punto de vista de su organización interna son como estructuras en las que todos sus miembros tuviesen el mismo desplazamiento respecto al origen. Desde un punto de vista funcional pueden ser considerados almacenamientos u objetos multi-uso.
En realidad las uniones son un recurso de programación que permite alojar distintos tipos de objetos en un mismo espacio cuando estos objetos no coexisten en el tiempo y no hay suficiente memoria disponible para asignarles espacios distintos. Se trata por tanto de objetos muy especializados en cuanto a su uso, propio de aplicaciones muy "afinadas", de tiempos en que la memoria era un bien escaso, pero cuya utilización real es bastante rara y desaconsejada.
§2 Declaración
La sintaxis de declaración es similar a las estructuras con las siguientes diferencias:
a: Las uniones pueden contener campos de bits ( 4.5.9), pero solo uno puede estar activo. Todos comienzan en el principio de la unión y como consecuencia, dado que los campos de bits son dependientes de la implementación, pueden presentar problemas para escribir código portable.
b: Un objeto que tenga constructor o destructor no puede ser utilizado como miembro de una unión.
c: A diferencia de las estructuras, en las uniones C++ no se permiten los especificadores de acceso public, private y protected de las clases ( 4.11.2a). Todos sus campos son públicos [2].
d: Las uniones solo pueden ser inicializadas en su declaración mediante su primer miembro. Ejemplo:
union local87 {
int i;
double d;
} a = { 20 };
§2.1 Ejemplo
union miUnion { // definición de unión de nombre miUnion
int i;
double d;
char ch;
char *ptr;
} mu, *muptr=μ // declara un objeto y un puntero al objeto
Aquí se ha definido un tipo nuevo de la clase unión, identificado como miUnion; se ha instanciado un objeto de dicha clase, denominado mu, que puede utilizarse para almacenar un int, (4 bytes), un double (8 bytes), o un char (1 byte), pero solo uno cada vez.
El uso debe ser consistente con los valores almacenados en cada caso, cuestión esta que queda bajo responsabilidad del programador (esta es la razón principal para que se desaconseje su uso salvo caso de necesidad insoslayable). Si se lee un dato de tipo distinto al que se escribió, el resultado obtenido es dependiente de la implementación.
Una unión no puede participar en la jerarquía de clases; no puede ser derivada de ninguna clase, ni ser una clase base. Aunque sí pueden tener un constructor ( 4.11.2d1) y ser miembros de clases.
§3 Acceso a miembros
Como en el caso de las estructuras, se dispone de dos operadores para referenciar los miembros de las uniones:
. Selector directo ( 4.9.16c Selector directo de miembro)
Una expresión del tipo Un.obj representa el objeto obj de la unión Un. La expresión es un Lvalue siempre que Un no lo sea y obj no sea una matriz.
-> Selector indirecto ( 4.9.16d Selector indirecto de miembro)
Una expresión del tipo uPtr -> obj representa el objeto obj de la unión Un siempre que uPtr sea un puntero a dicha unión. La expresión es un Lvalue siempre que obj no sea una matriz. Esta expresión es equivalente, y preferible, a (*uPtr).obj.
El uso de uno u otro selector es indiferente. Depende de que se tenga un identificador de la unión, en cuyo caso puede usarse el selector directo (expresión Un.obj), o un puntero, en cuyo caso puede usarse el indirecto (expresión uPtr->obj). En cualquier caso, es necesaria cierta atención como se muestra en el ejemplo (referido al objeto mu declarado anteriormente):
mu.d = 4.016; // Se inicia mu
printf("mu.d = %f\n",mu.d); //Ok. Salida: mu.d = 4.016
printf("mu.i = %d\n",mu.i); //?? resultado particular
mu.ch = 'A';
printf("mu.ch = %c\n",mu.ch); //Ok. Salida: mu.ch = A
printf("mu.d = %f\n",mu.d); //?? resultado particular
muptr->i = 3;
printf("mu.i = %d\n",mu.i); //Ok. Salida: mu.i = 3
El segundo printf es legal, dado que mu.i es un entero. El problema es que el patrón de bits que correspondería a mu.i coincide con parte del double previamente asignado en la primera sentencia, y su valor no tiene ningún sentido considerado aisladamente.
Como se ha señalado, las uniones comparten muchas características con las estructuras, incluyendo la notación. Además, con las uniones están permitidas las mismas operaciones que con las estructuras: asignación o copia como una unidad; obtener su dirección, y acceder a un miembro.
§4 Punteros a uniones
Cuando son modelados adecuadamente, los punteros a una unión señalan a cada uno de sus miembros y viceversa.
§5 Tamaño y alineación
Como en el resto de los objetos, su tamaño puede ser establecido en tiempo de ejecución con el operador sizeof ( 4.9.13) y como se ha señalado, corresponde con el mayor de los elementos que pueda almacenar. Lo mismo que en las estructuras, también aquí se producen alineaciones en las direcciones de memoria de los miembros.
Por ejemplo, si nos referimos a la unión anterior, las sentencias sizeof(union miUnion) y sizeof(mu), ambas devuelven 8; este es el tamaño en bytes de la unión, que corresponde a la opción más desfavorable, cuando se almacena un double, pero cuando mu almacena un int existen 4 bytes desaprovechados, y 7 cuando se almacena un char.
§6 Uniones anónimas
Uniones anónimas son aquellas que no tienen etiqueta y tampoco se utilizan para declarar un objeto nominado (con nombre). Tienen la forma general:
union { lista-de-miembros };
Sus miembros pueden ser accedidos directamente en el ámbito de su declaración sin necesidad de usar ninguno de los operadores de acceso x.y o p->y. Ejemplo:
#include <iostream.h>
void main () {
union { int i; double d; };
i = 25;
cout << "El valor de '?.i' es: " << i << endl;
}
Salida:
El valor de '?.i' es: 25
§6.1 Las uniones anónimas pueden ser globales, y anidadas o sin anidar [1].
Ejemplo:
#include <iostream.h>
static union { int i; double d; } ;
void main () {
i = 15;
cout << "El valor de '?.i' es: " << i << endl;
union { int i; double d; };
i = 25;
cout << "El valor de '?.i' es: " << i << endl;
cout << "El valor de '?.i' es: " << ::i << endl;
}
Salida:
El valor de '?.i' es: 15
El valor de '?.i' es: 25
El valor de '?.i' es: 15
§6.2 Las uniones anónimas no pueden tener funciones miembro ni miembros privados o protegidos (todos son públicos). Si son globales deben ser declaradas estáticas, y sin son locales pueden ser estáticas o automáticas ( 2.2.6). En otras palabras: las uniones anónimas no pueden tener enlazado externo ( 1.4.4).
Ejemplo:
#include <iostream.h>
union { int i; double d; } ;
void main () {
cout << "El valor '?.i' es: " << i << endl;
}
Resulado de la compilación:
Borland C++: Error E2020 pru1.c 2: Global anonymous union not static
Compilador GNU C++ 3.3.1 namespace-scope anonymous aggregates must be static
§7 Uniones anónimas anidadas
La estructura, clase o unión exterior de una unión anónima anidada debe tener un nombre. C++ permite uniones anónimas anidadas. Por ejemplo:
struct externa { // define estructura nominada externa
int x;
union { // unión anónima anidada
int y;
char c;
};
};
...
int main(void) {
struct externa ex1; // ex1 es una instancia de externa
return ex1.x + ex1.y;
}
§8 Uniones y otros tipos
Las uniones pueden aparecer en matrices, estructuras y como miembros de clases. A su vez, también pueden contener a dichos elementos. Ver ejemplo ( 4.9.20b).
[1] El ANSI-C nunca permite uniones anónimas, pero al ANSI C++ permite las tres clases: globales, anidadas, y no anidadas.
[2] Que todos sus campos sean públicos no es óbice para que ellas mismas no puedan ser miembros de clases con cualquier tipo de acceso (público, privado o protegido).
0 comentarios:
Publicar un comentario