2008-11-26 15 views
40

Tengo una estructura que creo un constructor personalizado para inicializar los miembros a los 0. He visto en compiladores más antiguos que cuando están en modo de lanzamiento, sin hacer un memset a 0, los valores no se inicializan.Inicializando una unión con un constructor no trivial

Ahora quiero usar esta estructura en una unión, pero obtengo errores porque tiene un constructor no trivial.

Entonces, pregunta 1. ¿El compilador implementado por defecto del constructor garantiza que todos los miembros de una estructura serán inicializados nulos? El constructor no trivial solo hace un memset de todos los miembros a '0' para asegurar una estructura limpia.

Pregunta 2: Si se debe especificar un constructor en la estructura base, ¿cómo se puede implementar una unión para contener ese elemento y garantizar un elemento base inicializado 0?

Respuesta

40

Pregunta 1: Los constructores predeterminados inicializan los miembros POD a 0 según el estándar C++. Ver el texto citado a continuación.

Pregunta 2: Si se debe especificar un constructor en una clase base, esa clase no puede formar parte de una unión.

Por último, se puede proporcionar un constructor para su unión:

union U 
{ 
    A a; 
    B b; 

    U() { memset(this, 0, sizeof(U)); } 
}; 

Para P1:

De C++ 03, 12.1 constructores, pg 190

El constructor por defecto definido implícitamente realiza el conjunto de inicializaciones de la clase que realizaría un constructor predeterminado escrito por el usuario para esa clase con una lista-inicializador-memoria vacía (12.6.2) y un cuerpo de función vacío.

De C++ 03, 8.5 Inicializadores, pg 145

Para default-inicializar un objeto de tipo T significa:

  • si T es un tipo de clase no POD (cláusula 9), el constructor predeterminado para T se llama (y la inicialización está mal formada si T no tiene un constructor predeterminado accesible);
  • si T es un tipo de matriz, cada elemento se inicializa por defecto;
  • de lo contrario, el objeto se inicializa en cero.

Para cero inicializar un objeto de tipo T significa:

  • si T es un tipo escalar (3.9), el objeto se establece en el valor de 0 (cero) convertido a T;
  • si T es un tipo de clase no-unión, cada miembro de datos no estático y cada subobjeto de clase base tiene cero inicializado;
  • si T es un tipo de unión, el primer miembro de datos con nombre del objeto se inicializa en cero;
  • si T es un tipo de matriz, cada elemento se inicializa en cero;
  • si T es un tipo de referencia, no se realiza ninguna inicialización.

Para Q2:

De C++ 03, 12.1 constructores, pg 190

Un constructor es trivial si se trata de un constructor por defecto declarado implícitamente -y si:

  • su clase no tiene funciones virtuales (10.3) ni clases base virtuales (10.1), y
  • todas las clases base directas de su clase tienen constructores triviales, y
  • para todos los miembros de datos no estáticos de su clase que son de tipo de clase (o del mismo array ), cada uno de tales clase tiene un constructor trivial

De C++ 03, 9,5 Uniones, pg 162

Una unión puede tener funciones miembro (incluidos constructores y destructores), pero no funciones virtuales (10.3). Una unión no debe tener clases base. Una unión no se utilizará como una clase base. 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 elemento no trivial. el operador de asignación de copia (13.5.3, 12.8) no puede ser miembro de una unión, ni puede una matriz de tales objetos

+21

Lo que falta aquí es que, a pesar del nombre, los constructores predeterminados no inicializan por defecto los miembros de POD. 12.6.2/4 dice lo que sucede cuando un miembro no se menciona en una lista de inicializadores, y según su cita de 12.1, esto se aplica a los ctors implícitos. Dice: "Si la entidad es un miembro de datos no estáticos de ... clase tipo ... y la clase de entidad es una clase no POD, la entidad se inicializa por defecto ... de lo contrario, la entidad no se inicializa ". Entonces, los miembros de datos POD no son inicializados por el constructor generado implícitamente. Los miembros de datos que no son POD son inicializados por defecto. –

+2

esta respuesta está desactualizada ... ver mi respuesta para C++ 11 debajo de –

0

¿Puedes hacer algo como esto?

class Outer 
{ 
public: 
    Outer() 
    { 
     memset(&inner_, 0, sizeof(inner_)); 
    } 
private: 
    union Inner 
    { 
     int qty_; 
     double price_; 
    } inner_; 
}; 

... o tal vez algo como esto?

union MyUnion 
{ 
    int qty_; 
    double price_; 
}; 

void someFunction() 
{ 
    MyUnion u = {0}; 
} 
+0

Lo habíamos considerado, pero la estructura que intentamos colocar en la unión está en uso en otras partes del código, por lo que se elimina el constructor (suponiendo que el compilador trata la estructura como POD y no inicializa todos los elementos a 0) podría romper código que depende de eso. – Superpolock

3

AFAIK miembros de la unión no pueden tener constructores o destructores.

Pregunta 1: no, no hay tal garantía. Cualquier miembro de POD que no esté en la lista de inicialización del constructor se inicializa por defecto, pero eso es con un constructor que usted define y tiene una lista de inicializadores. Si no define un constructor, o si define un constructor sin una lista de inicializadores y un cuerpo vacío, los miembros POD no se inicializarán.

Los miembros que no sean POD siempre se construirán a través de su constructor predeterminado, que si se sintetiza, de nuevo no inicializaría los miembros de POD. Dado que los miembros del sindicato pueden no tener constructores, se podría garantizar que los miembros de POD de las estructuras en una unión no serán inicializados.

Pregunta 2: siempre se puede inicializar estructuras/sindicatos, así:

struct foo 
{ 
    int a; 
    int b; 
}; 

union bar 
{ 
    int a; 
    foo f; 
}; 

bar b = { 0 }; 
+2

Puede otorgar a la unión un constructor que memset en sí mismo sea cero. –

+0

¡Buen punto! ¡Me sigo olvidando de los constructores de la unión yo mismo! –

+1

No hay diferencia entre el constructor predeterminado de un programador sin lista de inicializadores y el cuerpo vacío y un constructor generado por el compilador. –

-1

Vas a tener que esperar para C++ 0x a soportar por los compiladores para conseguir esto. Hasta entonces, lo siento.

2

Como se menciona en el comentario de Greg Rogers al post de unwesen, puede darle a su sindicato un constructor (y el destructor si lo desea):

struct foo 
{ 
    int a; 
    int b; 
}; 

union bar 
{ 
    bar() { memset(this, 0, sizeof(*this)); } 

    int a; 
    foo f; 
}; 
+0

Parece que necesito un poco de educación. ¿No memsetting el objeto a cero, eliminar la tabla virtual de clases? – EvilTeach

+4

@EvilTeach, Dos cosas, 1) no es necesario usar un vtable para implementar el polimorfismo (excepto que todos lo hacen). 2) ¿Ves algún método virtual en foo? O cualquier método en absoluto para el caso? ¿Hereda de algo? No hay vtable sin métodos virtuales. De hecho, si tuviéramos métodos virtuales y, por extensión, vtable, ya no sería un POD, y por lo tanto no elegible para la membresía en la unión. –

22

cosas cambiaron para mejor en C++ 11.

Ahora puede hacerlo legalmente, como described by Stroustrup (llegué a ese enlace desde Wikipedia article on C++11).

El ejemplo de Wikipedia es la siguiente:

#include <new> // Required for placement 'new'. 

struct Point { 
    Point() {} 
    Point(int x, int y): x_(x), y_(y) {} 
    int x_, y_; 
}; 

union U { 
    int z; 
    double w; 
    Point p; // Illegal in C++03; legal in C++11. 
    U() {new(&p) Point();} // Due to the Point member, a constructor 
          // definition is now *required*. 
}; 

BS entra en un poco más de detalle.

+0

Esto no parece funcionar en Visual Studio (2013) – maja

+0

Técnicamente, su consejo de que un constructor es obligatorio no es correcto. Al igual que cualquier otro miembro, solo se requiere si se usa. El almacenamiento en bruto se puede inicializar en un int, double o Point, y luego se puede usar un puntero a U para acceder a él (seguido del nombre de campo apropiado). Los usos de ejemplo incluyen la interpretación de una secuencia de objetos, la emulación de una pila o simplemente la interpretación de un valor asignado en el montón. – Yttrill

+0

@Yttrill si bien es cierto que no necesita definir un constructor a menos que construya una instancia de 'U', no creo que sea legal usar' U * 'para acceder a cualquier miembro de la unión si no hay unión el objeto ha sido construido. [basic.life] _Antes de que se haya iniciado el tiempo de vida de un objeto, pero después de que se haya asignado el almacenamiento que el objeto ocupará, cualquier [...] puntero que haga referencia a la ubicación [...] de almacenamiento puede desreferenciarse pero [.. .]. El programa tiene un comportamiento indefinido si [...] el puntero se usa para acceder a un miembro de datos no estáticos o llamar a una función miembro no estático del objeto_ – davmac

Cuestiones relacionadas