2009-04-21 19 views
8

Digamos que tengo una estructura definida como:¿Qué significa instanciar un objeto usando llaves en C++?

typedef 
struct number{ 
    int areaCode; 
    int prefix; 
    int suffix; 
} PhoneNumber; 

Cuando creo una instancia de esta estructura, si uso la siguiente sintaxis:

PhoneNumber homePhone = {858, 555, 1234}; 

... qué constructor se le llama? ¿El constructor predeterminado, o el constructor de copia, o ninguno en absoluto porque no está llamando 'nuevo'?

El verdadero propósito de esta pregunta es descubrir cómo puedo agregar un cuarto campo. Por eso quiero volver a definir mi estructura como:

typedef 
struct number{ 
    int areaCode; 
    int prefix; 
    int suffix; 
    int extension; // NEW FIELD INTRODUCED 
} PhoneNumber; 

Así que ahora, puedo crear nuevos objetos PhoneNumber con cuatro campos:

PhoneNumber officePhone = {858, 555, 6789, 777} 

Sin embargo, tengo cientos de estos casos PhoneNumber ya creados con solo 3 campos (xxx, xxx, xxxx). Así que no quiero revisar y modificar TODAS las instancias únicas de mi objeto PhoneNumber que ya está definido. Quiero poder dejarlos solos, pero aún así poder crear nuevas instancias de números telefónicos con CUATRO campos. Así que estoy tratando de descubrir cómo puedo sobreescribir el constructor para que no se rompan mis instancias de tres parámetros existentes, pero también soportará mis nuevas instancias de cuatro parámetros.

Cuando intento definir un constructor predeterminado que toma 3 campos y establece el cuarto en un valor predeterminado '0', obtengo errores (en la parte de instanciación del código, no en la definición del constructor) quejándose de que mi objeto debe ser inicializado por el constructor, no por {...}. ¿Entonces parece que si invalido el constructor predeterminado, ya no puedo usar llaves para crear mis nuevos objetos?

Lo sentimos si esto se aleja por completo de las preguntas originales.

+0

Relacionado: [http://stackoverflow.com/questions/88957/what-does-0-mean-in-c/88960#88960](http://stackoverflow.com/questions/ 88957/what-does-0-mean-in-c/88960 # 88960) –

Respuesta

1

Llama efectivamente al ctor predeterminado; lo que sucede es que se asigna una estructura y se asigna a cada valor el valor predeterminado utilizado "=".

+0

El constructor predeterminado no se llama, y ​​operator = tampoco se llama. En C++ 8.5.1 define esa sintaxis para la inicialización de agregados. –

+0

¿Leyó * lo que escribí? ¿Cuál es el valor predeterminado "="? –

2

Una estructura en C++ es como una clase. Se está llamando al constructor predeterminado. Después, cada campo se copia con su operador de asignación.

+0

El constructor predeterminado no se llama para tipos de POD y los campos no se copian. La sintaxis define la inicialización de los miembros del agregado. Ver C++ 8.5.1 –

4

Los miembros en realidad están inicializados con copia. El constructor predeterminado para cada uno no se llama y no se trata de operator= al contrario de lo que sugieren otras respuestas. Se puede mostrar mediante un software llamado geordi, alternativamente leyendo el Estándar. Mostraré la forma "divertida" de usar ese software. Tiene una clase tracked::B que puede mostrarnos cuando se invocan constructores/constructores de copia o operadores de asignación de copia/destructores. La salida que muestra es (TRACK límites de seguimiento a la instrucción siguiente a la misma):

B1*(B0) B1~ 

He utilizado este código

struct T { tracked::B b; }; int main() { tracked::B b; TRACK T t = { b }; } 

Como se ve, la segunda B objeto - que es el miembro dentro del local, variable t, se inicializa la copia desde el otro objeto b. Por supuesto, ningún operador de asignación está activado. Puede leer sobre esto en 12.6.1/2 en el Estándar, si lo desea.

Lo mismo, dicho sea de paso, es cierto con las matrices (que son también agregados). Muchas personas creen que los objetos que son miembros de matrices deben tener un tipo que tenga un constructor predeterminado. Pero eso no es verdad Pueden ser copiados inicializados por otro objeto de su tipo y funcionarán bien.

Todos otros elementos que no se inicializaron explícitamente en el agregado son valores inicializados. La inicialización del valor es una mezcla de inicialización predeterminada e inicialización cero. En realidad, si un miembro tiene un tipo que tiene un constructor declarado por el usuario, se invoca ese constructor. Si tiene un tipo que hace no tiene un constructor declarado por el usuario, entonces cada miembro del mismo tiene un valor inicializado. Para los tipos incorporados (int, bool, pointers, ...) una inicialización de valor es igual a la inicialización cero (eso significa que dicha variable se convertirá en cero). A continuación se inicializa cada miembro a cero - excepto el primero (a), que será uno:

struct T { int a, b, c; }; int main() { T t = { 1 }; } 

Dichas normas de inicialización son de miedo, de hecho - sobre todo porque la revisión de C++ 2003 introdujo que el valor de inicialización. Es wasn't part of the Standard a partir de 1998. Si está más interesado en esas llaves incluidas las inicializaciones, puede leer How to initialize nested structures in C++?.

3

No está llamando al ctor predeterminado, como han escrito otros. Conceptualmente es lo mismo, pero en la práctica, no encontrará ninguna llamada de función en el código de ensamblaje.

En cambio, los miembros permanecen sin inicializar; los estás inicializando con la construcción de llavero.

Curiosamente, esto:

PhoneNumber homePhone = {858, 555, 1234}; 

Los resultados en esta asamblea (GCC 4.0.1, -O0):

movl $858, -20(%ebp) 
movl $555, -16(%ebp) 
movl $1234, -12(%ebp) 

No muchas sorpresas allí. El ensamblado está alineado con la función que contiene la instrucción C++ anterior. Los valores (comenzando con $) se mueven (movl) en compensaciones a la pila (registro de ebp). Son negativos porque las ubicaciones de memoria para los miembros de la estructura preceden al código de inicialización.

Si no inicializa totalmente la estructura, es decir, dejar de lado algunos miembros de esta manera:

PhoneNumber homePhone = {858, 555}; 

... entonces me sale el siguiente código de montaje:

movl $0, -20(%ebp) 
movl $0, -16(%ebp) 
movl $0, -12(%ebp) 
movl $858, -20(%ebp) 
movl $555, -16(%ebp) 

parece como si el compilador hace algo muy similar a llamar al constructor predeterminado, seguido de la asignación. Pero de nuevo, esto está en línea en la función de llamada, no en una llamada de función.

Si por el contrario se define un constructor predeterminado que inicializa los miembros, a los valores indicados, así:

struct PhoneNumber { 
    PhoneNumber() 
    : areaCode(858) 
    , prefix(555) 
    , suffix(1234) 
    { 
    } 

    int areaCode; 
    int prefix; 
    int suffix; 
}; 

PhoneNumber homePhone; 

A continuación, se obtiene el código de montaje que realmente llama a una función, e inicializa los miembros de datos a través de un puntero a la estructura:

movl 8(%ebp), %eax 
movl $858, (%eax) 
movl 8(%ebp), %eax 
movl $555, 4(%eax) 
movl 8(%ebp), %eax 
movl $1234, 8(%eax) 

Cada línea que va movl 8(%ebp), %eax establece el valor del puntero (registro EAX) al principio de los datos de la estructura. En las otras líneas, eax se usa directamente, con un desplazamiento de 4 y un desplazamiento de 8, similar al direccionamiento directo de la pila en los dos ejemplos anteriores.

Por supuesto, todo esto es específico para la implementación del compilador, pero me sorprendería que otros compiladores hicieran algo extraordinariamente diferente.

+0

¿El uso del constructor predeterminado con la lista de inicialización de miembros genera una desreferencia de puntero si la optimización se genera? Es decir. con gcc, ¿la compilación con la opción -O3 elimina las referencias al puntero? – paxos1977

+0

Ver la salida del compilador no es un sustituto para comprender la especificación. –

+0

@ceretullis: No he buscado. @ don.neufeld: estuvo de acuerdo, pero eso no significa que sea inútil entender lo que realmente está sucediendo. –

0

Sin embargo, tengo cientos de estos casos PhoneNumber ya creadas con sólo 3 campos (xxx, xxx, xxxx). Así que no quiero pasar y modificar CADA instancia única de mi objeto PhoneNumber que ya está definido.

No hay problema, solo llama a update-instance-for-redefined-class y ..... er, nevermind. Proceda a marcar este "inútil"

2

La inicialización es, al menos para mí, una de las partes más difíciles del estándar C++. La sintaxis que está usando: Aggregate x = { 1, 2, 3, 4 }; define la inicialización de un tipo agregado con los primeros cuatro miembros asignados valores 1, 2, 3 y 4.

Como nota para su caso particular (la estructura ha crecido de 3 a 4 elementos), los campos no presentes en la lista de inicialización entre las llaves serán valor inicializado, que en particular para los tipos escalares es equivalente a cero inicialización que es lo mismo que asignar 0. Por lo tanto, se inicializará su cuarto elemento a 0.

Referencias

Todo está definido en el estándar de C++, capítulo 8.5, y más precisamente en 8.5.1 Agregados. Los agregados no se inicializarán con ningún constructor predeterminado declarado implícitamente. Si usa la sintaxis anterior, le está pidiendo al compilador que inicialice los campos dados con los valores proporcionados. Cualquier campo extra en el agregado será valor inicializado

Ahora, valor de inicialización se define como una llamada a la definida por el usuario constructor por defecto si el tipo es una clase con tal constructor. Si es una matriz o una clase sin unión sin un constructor definido por el usuario, entonces cada uno de los atributos del miembro será valor inicializado. De lo contrario el objeto será cero inicializan, que luego de nuevo se define como ...

Zero inicialización se define como establecer el valor a 0 para los tipos escalares. Para las clases, cada miembro de datos de clase y clase base será cero inicializado.Para uniones, el primer miembro (solo) será cero inicializado, para matrices todos los miembros serán cero inicializado.

0

No existe un constructor en su tipo, por lo que la sintaxis existente de tres parámetros debería modificarse en el sitio de inicialización agregado.

A menos que sea la semántica de su campo recién agregado, es decir. que el nuevo tipo de campo, se 'diseño-conscientes cero' (lenguaje recomendado por la programación .NET, Brad y Co etc en las directrices de diseño sin sentido), que

no puede:

a) proporcionar algo más significativo, ya sea como C++ parámetros por defecto si con un poco de magia que había un método utilizado (deault/params opcionales pronto estén disponibles en el mercado masivo C# tienda cerca de la tienda de comestibles web 4.0 y para el viejo COM)

ni se puede

b) siga el patrón similar al de C# para diseñar tipos de valor con marcadores de valor no válidos como 0 (que en este caso podría decirse que es v ery malo y malo en general si no tiene control sobre las constantes, algo que las bibliotecas de nivel de fuente hacen muy bien y todas las estructuras de JavaTM MS -like chupan).

En resumen, si 0 es un valor válido, se rellenará y algo que la mayoría de los escritores de compiladores consideraron útil antes de que existiera alguna vez un estilo o modismo administrado; pero no es necesariamente correcto.

De todos modos, tu mejor oportunidad es no C++, o C#. Es generación de código, es decir. una metaprogramación más liberal que la moderna 'creación de plantillas' de C++. La encontrará similar a la anotación de matriz JSON y podría usar cualquier espíritu o (mi consejo sería) rodar sus propios utils. A largo plazo, ayuda y, con el tiempo, querrás mejorar (es decir, lo que ellos llaman modelado cojo, también conocido como Oslo en .NET).

[problemas de este tipo en todos los idiomas, es un inconveniente de todos los de estilo C (C, C++, Java, C#, lo que sea); muy similar a las matrices de arreglos de hackers requeridas para cualquier tipo de trabajo de E/S semicontinuas o semiconomicas y evidentes incluso en tecnología web como SOAP, etc. Las nuevas variables C++ 0x no ayudan mucho aquí tampoco, pero aparentemente pueden traiga tiempos de compilación exponencialmente más rápidos si elige jugar con algún hacker de plantilla. A quién le importa :)]