2010-08-19 6 views
38

quiero usar una cuerda dentro de Union. si escribo como a continuación¿Por qué el compilador no permite std :: string dentro de la unión?

union U 
{ 
    int i; 
    float f; 
    string s; 
}; 

compilador da error diciendo T :: S tiene constructor de copia.

He leído alguna otra publicación para encontrar formas alternativas de resolver este problema. Pero quiero saber por qué el compilador no permite esto en primer lugar?

EDITAR: @KennyTM: En cualquier unión, si el miembro se inicializa, otros tendrán valores basura, si ninguno se inicializa, todos tendrán valores basura. Creo que la unión etiquetada solo proporciona cierta comodidad para acceder a los valores válidos de Union. Su pregunta: ¿cómo usted o el compilador escriben un constructor de copia para la unión anterior sin información adicional? sizeof (cadena) da 4 bytes. En base a esto, el compilador puede comparar el tamaño de otros miembros y asignar la asignación más grande (4 bytes en nuestro ejemplo). La longitud interna de la cuerda no importa porque se almacenará en una ubicación separada. Deje que la cuerda sea de cualquier longitud. Todo lo que la Unión tiene que saber es invocar el constructor de copia de clase de cadena con el parámetro de cadena. En cualquier forma en que el compilador encuentre que se debe invocar el constructor de copia en el caso normal, se seguirá un método similar incluso cuando la cadena esté dentro de Unión. Así que estoy pensando que el compilador podría hacer, asignar 4 bytes. Entonces, si se asigna una cadena a s, la clase de cadena se encargará de la asignación y copia de esa cadena utilizando su propio asignador. Entonces tampoco hay posibilidad de corrupción en la memoria.

¿La cadena no existe en el momento del desarrollo de la Unión en el compilador? Así que la respuesta aún no está clara para mí. Soy un nuevo amigo en este sitio, si hay algo mal, por favor discúlpeme.

+2

No use 'union' ya que es nuevo en C++,' union' es jugar con la memoria y eso es un dominio reservado a los expertos.Necesitas aprender acerca de C++ orientado a objetos para entender por qué sucede esto y te faltan demasiados conceptos en el momento para entender cualquiera de las respuestas dadas (en verdad pueden parecerle crípticas). Existen excelentes recursos disponibles, y varios temas en este sitio los enumeran. –

+2

soy nuevo en este sitio, no en C++. De todos modos ... Gracias a todos por sus esfuerzos y aportes. – bjskishore123

Respuesta

22

pensar en ello. ¿Cómo sabe el compilador qué tipo hay en la unión?

No es así. La operación fundamental de una unión es esencialmente un reparto de bits. Las operaciones con los valores contenidos en las uniones solo son seguras cuando cada tipo se puede llenar con basura. std::string no se puede, porque eso daría como resultado la corrupción de la memoria. Use boost::variant o boost::any.

+0

@Matthieu: Pls verifique la versión editada de la pregunta. Según mi opinión, no debería causar daños en la memoria. – bjskishore123

+3

@bjskishore: sizeof (cadena) no es portátil. Además, el compilador no puede encontrar qué constructor/etc para llamar porque no sabe qué tipo está en la unión. Todas las llamadas a funciones se deben conocer en tiempo de compilación o virtual, y una unión no es polimórfica ni constante en tiempo de compilación. Entonces el compilador nunca puede saber qué hacer. Además, el tamaño realmente no importa, el problema es que es un puntero. Si almacena un flotador en la unión y luego accede a una cadena, tendrá un flotador binario tratado como un puntero, lo que casi con seguridad apuntará a la memoria no válida y bloqueará su programa. – Puppy

+0

@Puppy, por favor, corrija la respuesta: el código OP solicitado funciona en C++ 11 y posterior. –

13

En C++ 98/03, los miembros de una unión no pueden tener constructores, destructores, funciones de miembros virtuales o clases base.

Así que, básicamente, sólo se puede utilizar una función de los tipos de datos, o PODs

Nota que está cambiando en C++ 0x: Unrestricted unions

union { 
    int z; 
    double w; 
    string s; // Illegal in C++98, legal in C++0x. 
}; 
+2

+1 para obtener información sobre C++ 0x. Gracias. – bjskishore123

45

Porque tener una clase con un constructor no trivial (copy /) en una unión no tiene sentido. Supongamos que tenemos

union U { 
    string x; 
    vector<int> y; 
}; 

U u; // <-- 

Si U era una estructura, u.x y u.y podría ser inicializado a una cadena vacía y el vector vacío, respectivamente. Pero los miembros de un sindicato comparten la misma dirección. Por lo tanto, si se inicializa u.x, u.y contendrá datos no válidos, al igual que el reverso. Si ambos no se inicializan, entonces no pueden ser utilizados. En cualquier caso, tener estos datos en una unión no se puede manejar fácilmente, por lo que C++ 98 opta por negar esto: (§ 9.5/1):

Un objeto de una clase con un constructor no trivial (12.1), un constructor de copia no trivial (12.8), un destructor no trivial (12.4), o un no trivial el operador de asignación de copias (13.5.3, 12.8) no puede ser miembro de una unión, ni puede una matriz de dichos objetos.

En C++ 0x esta regla se ha relajado (§ 9,5/2):

A lo sumo uno de los miembros de datos no estático de una unión puede tener una abrazadera-o-igual- inicializador. [Nota: si cualquier miembro de una unión no estático tiene un constructor predeterminado no trivial (12.1), constructor de copia (12.8), constructor de movimiento (12.8), operador de asignación de copia (12.8), operador de asignación de movimiento (12.8), o destructor (12.4), la función miembro correspondiente de la unión debe ser proporcionada por el usuario o se eliminará implícitamente (8.4.3) para la unión. - nota final]

pero sigue siendo un posible crear (correctos) CON/destructores de la unión, por ejemplo, ¿Cómo escribes tú o el compilador un constructor de copias para la unión anterior sin información adicional? Para asegurarse de qué miembro de la unión está activo, necesita un tagged union, y debe manejar la construcción y la destrucción de forma manual, p.

struct TU { 
    int type; 
    union { 
    int i; 
    float f; 
    std::string s; 
    } u; 

    TU(const TU& tu) : type(tu.type) { 
    switch (tu.type) { 
     case TU_STRING: new(&u.s)(tu.u.s); break; 
     case TU_INT: u.i = tu.u.i;  break; 
     case TU_FLOAT: u.f = tu.u.f;  break; 
    } 
    } 
    ~TU() { 
    if (tu.type == TU_STRING) 
     u.s.~string(); 
    } 
    ... 
}; 

Pero, como se ha mencionado @DeadMG, esto ya se implementa como boost::variantorboost::any.

+0

explicación increíble! – Chubsdad

+0

¿Cuál es la definición de constructores triviales o no triviales? –

+0

@SiverSun trivial son aquellas que el compilador crea para usted, no triviales las que codifica. @KennyTM, ¡gran respuesta! – FireAphis

7

Desde el C++ §9.5.1 spec:

Un objeto de una clase con un constructor no trivial, una copia constructor no trivial, un destructor no trivial, o un no trivial el operador de asignación de copias no puede ser miembro de una unión.

La razón de esta regla es que el compilador no sabrá cuál de los destructores/llamar a los constructores, ya que nunca se sabe realmente cuál de los posibles objetos que hay dentro de la unión.

0

La basura se introduce si

  1. asigna una cadena
  2. continuación, asignar un int o flotar
  3. entonces una cadena de nuevo

cadena gestiona la memoria en otro lugar. Esta información probablemente sea un puntero. Este puntero está guardado cuando se asigna el int. Asignar una nueva cadena debería destruir la cadena anterior, lo cual no es posible.

El segundo paso debe destruir la cadena, pero no sabe, si ha habido una cadena.

Obviamente, han encontrado una solución para este problema mientras tanto.

+0

Esta pregunta tiene más de 5 años. Una respuesta ahora también debería tratar con los cambios que se hicieron en C++ 11 y cómo 'std :: string' [podría ser miembro de una unión ahora] (http://en.cppreference.com/w/cpp/language/union). Quizás algún código de ejemplo sería una buena idea. – Niall

+0

Algunas personas se sorprenden con versiones antiguas. –

Cuestiones relacionadas