palabras conjuntos clase c++ c unions

c++ - conjuntos - ¿Cuándo alguien usaría una unión? ¿Es un remanente de los días de solo C?



union de conjuntos c++ (18)

Algunos usos para los sindicatos:

  • Proporcione una interfaz de endianness general a un host externo desconocido.
  • Manipule datos de coma flotante de la arquitectura de CPU extranjera, como aceptar VAX G_FLOATS desde un enlace de red y convertirlos a IEEE 754 long reals para su procesamiento.
  • Proporcione acceso directo a bit bit a un tipo de nivel superior.

union { unsigned char byte_v[16]; long double ld_v; }

Con esta declaración, es simple mostrar los valores de bytes hexadecimales de un long double , cambiar el signo del exponente, determinar si es un valor denormal, o implementar una aritmética doble larga para una CPU que no lo admite, etc.

  • Ahorro de espacio de almacenamiento cuando los campos dependen de ciertos valores:

    class person { string name; char gender; // M = male, F = female, O = other union { date castrated; // for males int pregnancies; // for females } gender_specific_data; }

  • Amplia los archivos de inclusión para usar con tu compilador. Encontrarás docenas y cientos de usos de union :

    [wally@zenetfedora ~]$ cd /usr/include [wally@zenetfedora include]$ grep -w union * a.out.h: union argp.h: parsing options, getopt is called with the union of all the argp bfd.h: union bfd.h: union bfd.h:union internal_auxent; bfd.h: (bfd *, struct bfd_symbol *, int, union internal_auxent *); bfd.h: union { bfd.h: /* The value of the symbol. This really should be a union of a bfd.h: union bfd.h: union bfdlink.h: /* A union of information depending upon the type. */ bfdlink.h: union bfdlink.h: this field. This field is present in all of the union element bfdlink.h: the union; this structure is a major space user in the bfdlink.h: union bfdlink.h: union curses.h: union db_cxx.h:// 4201: nameless struct/union elf.h: union elf.h: union elf.h: union elf.h: union elf.h:typedef union _G_config.h:typedef union gcrypt.h: union gcrypt.h: union gcrypt.h: union gmp-i386.h: union { ieee754.h:union ieee754_float ieee754.h:union ieee754_double ieee754.h:union ieee854_long_double ifaddrs.h: union jpeglib.h: union { ldap.h: union mod_vals_u { ncurses.h: union newt.h: union { obstack.h: union pi-file.h: union { resolv.h: union { signal.h:extern int sigqueue (__pid_t __pid, int __sig, __const union sigval __val) stdlib.h:/* Lots of hair to allow traditional BSD use of `union wait'' stdlib.h: (__extension__ (((union { __typeof(status) __in; int __i; }) / stdlib.h:/* This is the type of the argument to `wait''. The funky union stdlib.h: causes redeclarations with either `int *'' or `union wait *'' to be stdlib.h:typedef union stdlib.h: union wait *__uptr; stdlib.h: } __WAIT_STATUS __attribute__ ((__transparent_union__)); thread_db.h: union thread_db.h: union tiffio.h: union { wchar.h: union xf86drm.h:typedef union _drmVBlank {

He aprendido pero realmente no entiendo los sindicatos. Cada texto en C o C ++ que paso los introduce (a veces de pasada), pero tienden a dar muy pocos ejemplos prácticos de por qué o dónde usarlos. ¿Cuándo serían útiles los sindicatos en un caso moderno (o incluso heredado)? Mi único dos conjeturas sería programar microprocesadores cuando tienes un espacio muy limitado para trabajar, o cuando estás desarrollando una API (o algo similar) y quieres forzar al usuario final a tener solo una instancia de varios objetos / tipos en una vez. ¿Son estas dos conjeturas incluso cerca de la derecha?


Bueno, un ejemplo de caso de uso que puedo pensar es este:

typedef union { struct { uint8_t a; uint8_t b; uint8_t c; uint8_t d; }; uint32_t x; } some32bittype;

A continuación, puede acceder a las partes separadas de 8 bits de ese bloque de datos de 32 bits; sin embargo, prepárese para ser potencialmente mordido por la endianidad.

Este es solo un ejemplo hipotético, pero siempre que quiera dividir los datos en un campo en partes componentes como esta, podría usar una unión.

Dicho esto, también hay un método que es endian-safe:

uint32_t x; uint8_t a = (x & 0xFF000000) >> 24;

Por ejemplo, dado que el compilador convertirá esa operación binaria a la endianidad correcta.


De http://cplus.about.com/od/learningc/ss/lowlevel_9.htm :

Los usos de la unión son pocos y distantes. En la mayoría de las computadoras, el tamaño de un puntero y un int generalmente son los mismos; esto se debe a que ambos suelen caber en un registro en la CPU. Entonces, si quieres hacer un lanzamiento rápido y sucio de un puntero a una int o de otra manera, declara una unión.

union intptr { int i; int * p; }; union intptr x; x.i = 1000; /* puts 90 at location 1000 */ *(x.p)=90;

Otro uso de una unión es en un comando o protocolo de mensaje donde se envían y reciben mensajes de diferentes tamaños. Cada tipo de mensaje contendrá información diferente, pero cada uno tendrá una parte fija (probablemente una estructura) y un bit de parte variable. Así es como puedes implementarlo ...

struct head { int id; int response; int size; }; struct msgstring50 { struct head fixed; char message[50]; } struct

struct msgstring80 {struct head fixed; mensaje de char [80]; }
struct msgint10 {struct head fixed; mensaje int [10]; } struct msgack {struct head fixed; int ok; } union messagetype {
struct msgstring50 m50; struct msgstring80 m80; struct msgint10 i10; struct msgack ack; }

En la práctica, aunque las uniones son del mismo tamaño, tiene sentido enviar solo los datos significativos y no desperdiciar espacio. Un msgack tiene solo 16 bytes de tamaño, mientras que un msgstring80 tiene 92 bytes. Entonces, cuando se inicializa una variable messagetype, tiene su campo de tamaño establecido según el tipo que sea. Esto puede ser utilizado por otras funciones para transferir el número correcto de bytes.


Del artículo de Wikipedia sobre sindicatos :

La principal utilidad de una unión es ahorrar espacio , ya que proporciona una forma de permitir que se almacenen muchos tipos diferentes en el mismo espacio. Los sindicatos también proporcionan polimorfismo crudo . Sin embargo, no hay verificación de tipos, por lo que depende del programador asegurarse de acceder a los campos apropiados en diferentes contextos. El campo relevante de una variable de unión generalmente está determinado por el estado de otras variables, posiblemente en una estructura adjunta.

Un lenguaje común de programación C usa uniones para realizar lo que C ++ llama a un reinterpret_cast, asignando a un campo de una unión y leyendo de otro, como se hace en un código que depende de la representación cruda de los valores.


Digamos que tiene n diferentes tipos de configuraciones (simplemente es un conjunto de variables que definen los parámetros). Al usar una enumeración de los tipos de configuración, puede definir una estructura que tenga el ID del tipo de configuración, junto con una unión de todos los diferentes tipos de configuraciones.

De esta forma, donde sea que pase la configuración puede usar el ID para determinar cómo interpretar los datos de configuración, pero si las configuraciones fueran enormes, no se vería forzado a tener estructuras paralelas para cada tipo de espacio de pérdida de potencial.


En los primeros días de C (por ejemplo, como se documentó en 1974), todas las estructuras compartían un espacio de nombres común para sus miembros. Cada nombre de miembro se asoció con un tipo y un desplazamiento; si "wd_woozle" fuera un "int" en el desplazamiento 12, entonces dado un puntero p de cualquier tipo de estructura, p->wd_woozle sería equivalente a *(int*)(((char*)p)+12) . El lenguaje requería que todos los miembros de todos los tipos de estructuras tuvieran nombres únicos, excepto que permitía explícitamente la reutilización de los nombres de los miembros en los casos en que cada estructura donde se usaban los trataba como una secuencia inicial común.

El hecho de que los tipos de estructura se puedan usar promiscuamente hizo posible que las estructuras se comporten como si contuvieran campos superpuestos. Por ejemplo, definiciones dadas:

struct float1 { float f0;}; struct byte4 { char b0,b1,b2,b3; }; /* Unsigned didn''t exist yet */

el código podría declarar una estructura de tipo "float1" y luego usar "members" b0 ... b3 para acceder a los bytes individuales allí. Cuando se cambió el idioma para que cada estructura recibiera un espacio de nombres separado para sus miembros, se rompería el código que dependía de la capacidad de acceder a las cosas de múltiples maneras. Los valores de separar espacios de nombres para diferentes tipos de estructura eran suficientes para requerir que dicho código se cambiara para acomodarlo, pero el valor de tales técnicas era suficiente para justificar la extensión del lenguaje para continuar apoyándolo.

El código que se había escrito para explotar la capacidad de acceder al almacenamiento dentro de una struct float1 como si fuera una struct byte4 podría hacerse funcionar en el nuevo idioma agregando una declaración: union f1b4 { struct float1 ff; struct byte4 bb; }; union f1b4 { struct float1 ff; struct byte4 bb; }; , declarando objetos como type union f1b4; en lugar de struct float1 , y reemplazando accesos a f0 , b0 , b1 , etc. con ff.f0 , bb.b0 , bb.b1 , etc. Si bien hay mejores formas en que se podría haber respaldado dicho código, el enfoque union fue al menos algo realizable, al menos con interpretaciones de la era C89 de las reglas de aliasing.


Encuentro las uniones de C ++ geniales. Parece que la gente generalmente solo piensa en el caso de uso en el que uno quiere cambiar el valor de una instancia sindical "en su lugar" (que, al parecer, sirve solo para guardar memoria o realizar conversiones dudosas).

De hecho, los sindicatos pueden ser de gran utilidad como herramienta de ingeniería de software, incluso cuando nunca cambia el valor de ninguna instancia de unión .

Caso de uso 1: el camaleón

Con los sindicatos, puede reagrupar una cantidad de clases arbitrarias bajo una denominación, lo que no es sin similitudes con el caso de una clase base y sus clases derivadas. Sin embargo, lo que cambia es lo que puede y no puede hacer con una instancia de unión determinada:

struct Batman; struct BaseballBat; union Bat { Batman brucewayne; BaseballBat club; }; ReturnType1 f(void) { BaseballBat bb = {/* */}; Bat b; b.club = bb; // do something with b.club } ReturnType2 g(Bat& b) { // do something with b, but how do we know what''s inside? } Bat returnsBat(void); ReturnType3 h(void) { Bat b = returnsBat(); // do something with b, but how do we know what''s inside? }

Parece que el programador tiene que estar seguro del tipo de contenido de una instancia sindical determinada cuando quiere usarlo. Es el caso en la función f anterior. Sin embargo, si una función recibiera una instancia de unión como argumento pasado, como es el caso de g anterior, entonces no sabría qué hacer con ella. Lo mismo se aplica a las funciones que devuelven una instancia de unión, vea h : ¿cómo sabe la persona que llama qué hay dentro?

Si una instancia de unión nunca se pasa como argumento o como valor de retorno, es probable que tenga una vida muy monótona, con picos de emoción cuando el programador elige cambiar su contenido:

Batman bm = {/* */}; Baseball bb = {/* */}; Bat b; b.brucewayne = bm; // stuff b.club = bb;

Y ese es el caso de uso más (no popular) de los sindicatos. Otro caso de uso es cuando una instancia de unión viene con algo que le dice su tipo.

Caso de uso 2: "Encantado de conocerte, soy object , de Class "

Supongamos que un programador eligiera emparejar siempre una instancia sindical con un descriptor de tipo (dejaré a discreción del lector imaginar una implementación para uno de esos objetos). Esto frustra el propósito de la unión en sí misma, si lo que el programador desea es ahorrar memoria y que el tamaño del descriptor de tipo no es insignificante con respecto a la unión. Pero supongamos que es crucial que la instancia sindical se transmita como argumento o como valor de retorno cuando el destinatario o la persona que llama no sepa qué contiene.

Luego el programador tiene que escribir una declaración de flujo de control de switch para distinguir a Bruce Wayne de un palo de madera, o algo equivalente. No es tan malo cuando solo hay dos tipos de contenidos en la unión, pero obviamente, la unión ya no escala.

Caso de uso 3:

Como lo resumieron los autores de una recomendación para el estándar ISO C ++ en 2008,

Muchos dominios de problemas importantes requieren grandes cantidades de objetos o recursos limitados de memoria. En estas situaciones, la conservación del espacio es muy importante, y una unión es a menudo una forma perfecta de hacerlo. De hecho, un caso de uso común es la situación en la que una unión nunca cambia su miembro activo durante su vigencia. Se puede construir, copiar y destruir como si fuera una estructura que contiene solo un miembro. Una aplicación típica de esto sería crear una colección heterogénea de tipos no relacionados que no están asignados dinámicamente (quizás están construidos in situ en un mapa, o miembros de una matriz).

Y ahora, un ejemplo, con un diagrama de clase UML:

La situación en inglés simple: un objeto de clase A puede tener objetos de cualquier clase entre B1, ..., Bn y, como máximo, uno de cada tipo, siendo n un número bastante grande, digamos al menos 10.

No queremos agregar campos (miembros de datos) a A así:

private: B1 b1; . . . Bn bn;

porque n puede variar (es posible que deseemos agregar clases Bx a la mezcla), y porque esto causaría un lío con los constructores y porque los objetos A ocuparían mucho espacio.

Podríamos usar un curioso contenedor de punteros void* para objetos Bx con moldes para recuperarlos, pero eso es fugly y tan estilo C ... pero lo más importante es que nos dejaría con la vida útil de muchos objetos dinámicamente asignados para administrar.

En cambio, lo que se puede hacer es esto:

union Bee { B1 b1; . . . Bn bn; }; enum BeesTypes { TYPE_B1, ..., TYPE_BN }; class A { private: std::unordered_map<int, Bee> data; // C++11, otherwise use std::map public: Bee get(int); // the implementation is obvious: get from the unordered map };

Luego, para obtener el contenido de una instancia de unión a partir de data , usa a.get(TYPE_B2).b2 y los me gusta, donde a es una instancia de clase A

Esto es aún más poderoso ya que los sindicatos no tienen restricciones en C ++ 11. Consulte el documento vinculado a arriba o este artículo para más detalles.


La palabra clave union , aunque todavía se usa en C ++ 03 1 , es principalmente un remanente de los C días. El problema más evidente es que solo funciona con POD 1 .

La idea de la unión, sin embargo, todavía está presente, y de hecho las bibliotecas de Boost presentan una clase similar a un sindicato:

boost::variant<std::string, Foo, Bar>

Que tiene la mayoría de los beneficios de la union (si no todos) y agrega:

  • capacidad de usar correctamente tipos que no sean POD
  • seguridad de tipo estático

En la práctica, se ha demostrado que era equivalente a una combinación de union + enum , y se estableció como referencia que era tan rápido (mientras boost::any es más del reino de dynamic_cast , ya que usa RTTI).

1 Las uniones se actualizaron en C ++ 11 ( uniones no restringidas ), y ahora pueden contener objetos con destructores, aunque el usuario tiene que invocar el destructor de forma manual (en el miembro de la unión actualmente activo). Aún es mucho más fácil usar variantes.


Los sindicatos generalmente se usan con la compañía de un discriminador: una variable que indica cuál de los campos de la unión es válido. Por ejemplo, supongamos que quieres crear tu propio tipo de Variant :

struct my_variant_t { int type; union { char char_value; short short_value; int int_value; long long_value; float float_value; double double_value; void* ptr_value; }; };

Entonces lo usarías como:

/* construct a new float variant instance */ void init_float(struct my_variant_t* v, float initial_value) { v->type = VAR_FLOAT; v->float_value = initial_value; } /* Increments the value of the variant by the given int */ void inc_variant_by_int(struct my_variant_t* v, int n) { switch (v->type) { case VAR_FLOAT: v->float_value += n; break; case VAR_INT: v->int_value += n; break; ... } }

Esto es realmente una expresión bastante común, especialmente en las partes internas de Visual Basic.

Para ver un ejemplo real, vea la unión SDL_Event de SDL. ( código fuente actual aquí ). Hay un campo de type en la parte superior de la unión, y el mismo campo se repite en cada estructura de evento SDL_ *. Luego, para manejar el evento correcto, necesita verificar el valor del campo type .

Los beneficios son simples: hay un solo tipo de datos para manejar todos los tipos de eventos sin usar memoria innecesaria.


Los sindicatos proporcionan polimorfismo en C.


Los sindicatos proporcionan una forma de manipular diferentes tipos de datos en un área única de almacenamiento sin incorporar ninguna información independiente de la máquina en el programa. Son análogos a los registros variantes en pascal.

Como ejemplo, como podría encontrarse en un administrador de tablas de símbolos de compilación, supongamos que una constante puede ser un int, un float o un puntero de carácter. El valor de una constante particular debe almacenarse en una variable del tipo apropiado; sin embargo, es más conveniente para la administración de la tabla si el valor ocupa la misma cantidad de almacenamiento y se almacena en el mismo lugar, independientemente de su tipo. Este es el propósito de una unión: una variable única que puede contener legítimamente uno de varios tipos. La sintaxis se basa en estructuras:

union u_tag { int ival; float fval; char *sval; } u;

La variable u será lo suficientemente grande como para contener el mayor de los tres tipos; el tamaño específico depende de la implementación. Cualquiera de estos tipos puede asignarse a usted y luego usarse en expresiones, siempre que el uso sea consistente


Los sindicatos son útiles cuando se trata de datos a nivel de byte (bajo nivel).

Uno de mis últimos usos fue el modelado de direcciones IP que se ve a continuación:

// Composite structure for IP address storage union { // IPv4 @ 32-bit identifier // Padded 12-bytes for IPv6 compatibility union { struct { unsigned char _reserved[12]; unsigned char _IpBytes[4]; } _Raw; struct { unsigned char _reserved[12]; unsigned char _o1; unsigned char _o2; unsigned char _o3; unsigned char _o4; } _Octet; } _IPv4; // IPv6 @ 128-bit identifier // Next generation internet addressing union { struct { unsigned char _IpBytes[16]; } _Raw; struct { unsigned short _w1; unsigned short _w2; unsigned short _w3; unsigned short _w4; unsigned short _w5; unsigned short _w6; unsigned short _w7; unsigned short _w8; } _Word; } _IPv6; } _IP;


Me gustaría agregar un buen ejemplo práctico para usar Union: implementar fórmula calculadora / intérprete o usar algún tipo de computación (por ejemplo, desea usar modificable durante las partes de tiempo de ejecución de sus fórmulas de computación, resolver ecuación numéricamente) solo por ejemplo). Por lo tanto, es posible que desee definir números / constantes de diferentes tipos (entero, punto flotante, incluso números complejos) como este:

struct Number{ enum NumType{int32, float, double, complex}; NumType num_t; union{int ival; float fval; double dval; ComplexNumber cmplx_val} }

Así que está guardando memoria y, lo que es más importante, evita las asignaciones dinámicas para una cantidad probablemente extrema (si usa muchos números definidos en tiempo de ejecución) de objetos pequeños (en comparación con las implementaciones a través de la herencia de clase / polimorfismo). Pero lo que es más interesante, todavía puedes usar el poder del polimorfismo C ++ (si eres fanático del doble despacho, por ejemplo;) con este tipo de estructura. Simplemente agregue el puntero de interfaz "ficticio" a la clase principal de todos los tipos de números como un campo de esta estructura, apuntando a esta instancia en lugar de / además del tipo sin procesar, o use buenos punteros antiguos de función C.

struct NumberBase { virtual Add(NumberBase n); ... } struct NumberInt: Number { //implement methods assuming Number''s union contains int NumberBase Add(NumberBase n); ... } struct NumberDouble: Number { //implement methods assuming Number''s union contains double NumberBase Add(NumberBase n); ... } //e.t.c. for all number types/or use templates struct Number: NumberBase{ union{int ival; float fval; double dval; ComplexNumber cmplx_val;} NumberBase* num_t; Set(int a) { ival=a; //still kind of hack, hope it works because derived classes of Number dont add any fields num_t = static_cast<NumberInt>(this); } }

para que pueda usar el polimorfismo en lugar de las comprobaciones de tipo con el interruptor (tipo) - con una implementación eficiente de la memoria (sin asignación dinámica de objetos pequeños) - si lo necesita, por supuesto.


Un ejemplo cuando he usado una unión:

class Vector { union { double _coord[3]; struct { double _x; double _y; double _z; }; }; ... }

esto me permite acceder a mis datos como una matriz o los elementos.

He usado una unión para que los diferentes términos apunten al mismo valor. En el procesamiento de imágenes, ya sea que esté trabajando en columnas o ancho o el tamaño en la dirección X, puede ser confuso. Para solucionar este problema, utilizo una unión para saber qué descripciones van juntas.

union { // dimension from left to right // union for the left to right dimension uint32_t m_width; uint32_t m_sizeX; uint32_t m_columns; }; union { // dimension from top to bottom // union for the top to bottom dimension uint32_t m_height; uint32_t m_sizeY; uint32_t m_rows; };


Un ejemplo es en el ámbito embebido, donde cada bit de un registro puede significar algo diferente. Por ejemplo, una unión de un entero de 8 bits y una estructura con 8 campos de bits separados de 1 bit le permite cambiar un bit o todo el byte.


Un impulso reciente en la, ya elevada, importancia de la unión ha sido dada por la Regla de Aliasing estricta introducida en la versión más reciente de C estándar.

Puede usar uniones para hacer type-punning sin violar el estándar C.
Este programa tiene un comportamiento no especificado (porque he supuesto que float y unsigned int tienen la misma longitud) pero no un comportamiento indefinido (ver here ).

#include <stdio.h> union float_uint { float f; unsigned int ui; }; int main() { float v = 241; union float_uint fui = {.f = v}; //May trigger UNSPECIFIED BEHAVIOR but not UNDEFINED BEHAVIOR printf("Your IEEE 754 float sir: %08x/n", fui.ui); //This is UNDEFINED BEHAVIOR as it violates the Strict Aliasing Rule unsigned int* pp = (unsigned int*) &v; printf("Your IEEE 754 float, again, sir: %08x/n", *pp); return 0; }


Un uso brillante de la unión es la alineación de memoria, que encontré en el código fuente PCL (Point Cloud Library). La estructura de datos única en la API puede apuntar a dos arquitecturas: CPU con soporte SSE, así como la CPU sin soporte SSE. Por ejemplo: la estructura de datos para PointXYZ es

typedef union { float data[4]; struct { float x; float y; float z; }; } PointXYZ;

Los 3 flotadores están acolchados con un flotador adicional para alineación SSE. Entonces para

PointXYZ point;

El usuario puede acceder a point.data [0] o point.x (dependiendo del soporte de SSE) para acceder, por ejemplo, a la coordenada x. Más detalles de mejor uso se encuentran en el siguiente enlace: documentación de PCL Tipos de PointT


Herb Sutter escribió en GOTW unos seis años, con énfasis agregado:

"Pero no crean que las uniones son solo un vestigio de tiempos pasados. Las uniones son quizás más útiles para ahorrar espacio al permitir que los datos se superpongan, y esto todavía es deseable en C ++ y en el mundo moderno de hoy. Por ejemplo, algunos de los más las implementaciones avanzadas de bibliotecas estándar de C ++ ahora usan esta técnica para implementar la "optimización de cadenas pequeñas", una gran alternativa de optimización que reutiliza el almacenamiento dentro de un objeto de cadena: para cadenas grandes, el espacio dentro del objeto cadena almacena el puntero habitual para el búfer dinámicamente asignado y la información de limpieza como el tamaño del búfer, para cadenas pequeñas, el mismo espacio se reutiliza para almacenar los contenidos de cadena directamente y evitar por completo cualquier asignación de memoria dinámica. Para obtener más información sobre la optimización de cadena pequeña (y otras optimizaciones de cadenas) y pesimismo en profundidad considerable), ver ... "

Y para un ejemplo menos útil, vea la pregunta larga pero inconclusa gcc, el alias estricto y el lanzamiento a través de una unión .