2012-08-31 23 views
43

Estaba escribiendo código hoy y obtuve un error de compilación extraño, que parece ser causado por la inicialización de las variables miembro en un orden diferente al que se declararon.¿Por qué debería inicializar las variables de miembro en el orden en que están declaradas?

Ejemplo:

class Test { 
    int a; 
    int b; 

public: 
    Test() : b(1), a(2) { 
    } 
}; 

int main() { 
    Test test; 
    return 0; 
} 

Entonces, si compilo con -Werror -Wall:

$ g++ -Werror -Wall test.cpp 
test.cpp: In constructor ‘Test::Test()’: 
test.cpp:3:9: error: ‘Test::b’ will be initialized after [-Werror=reorder] 
test.cpp:2:9: error: ‘int Test::a’ [-Werror=reorder] 
test.cpp:6:5: error: when initialized here [-Werror=reorder] 
cc1plus: all warnings being treated as errors 

que dan cuenta de que -Wall está pidiendo explícitamente GCC ir over-the-top con advertencias, pero supongo que hay una razón para todos ellos. Entonces, ¿cómo podría importar el orden de las variables iniciales de los miembros?

+2

-Werror, como indican los mensajes, le dice al compilador que trate todas las advertencias como errores. El código tal como está escrito es válido y su significado está bien definido (aunque hay argumentos razonables para no escribirlo de esa manera), pero con -Werror, el compilador no cumple con el estándar porque se niega a compilar un código válido. –

Respuesta

71

La razón es porque se inicializaron en el orden en que se declararon en su clase, no en el orden en que los inicializó en el constructor y le advierten que no se usará el orden de su constructor.

Esto es para ayudar a prevenir errores donde la inicialización de b depende de a o viceversa.

La razón de este orden es porque solo hay un destructor, y tiene que elegir un "orden inverso" para destruir el miembro de la clase. En este caso, la solución más simple fue usar el orden de declaración dentro de la clase para asegurarse de que los atributos siempre se destruyeran en el orden inverso correcto.

27

No debe, ya que disminuye la legibilidad y es potencialmente engañoso.

Si lo hizo:

Test() : b(1), a(b) {} 

parecería que b continuación a se establecen en 1, mientras que en realidad el valor inicializado de b se utiliza para inicializar a antes b se inicializa a 1.

+2

+1 para que el ejemplo muestre por qué el pedido realmente importa y qué puede salir mal en caso de que alguien suponga que el pedido es lo que han declarado en el constructor. – atuljangra

12

En realidad, el compilador siempre inicializa las variables en el orden de declaración, incluso si escribe los inicializadores en un orden diferente. Por lo tanto, si no escribe las inicializaciones en el orden de declaración, el orden de los inicializadores no se ajusta al orden de inicialización, lo que puede generar errores sutiles si las inicializaciones dependen una de la otra.

Por ejemplo, considere el código

Test(): b(42), a(b) {} 

Esto es un error porque a se inicializa antes de b, pero se ve OK. Si usted lo escribe en el orden de declaración (que es del orden de inicialización), el error hace obvio:

Test(): a(b), b(42) {} 

Tenga en cuenta que el fallo también puede ser más sutil que eso; por ejemplo imagine a y b son tipos de clases que generan algo en su constructor; luego, con el orden "incorrecto", pensaría que la salida b debería aparecer antes del a, cuando en realidad sucederá lo contrario. Si la salida a aparece primero dará lugar a un archivo no válido, eso también es un error, pero no hay manera de que el compilador pueda notar el problema si los constructores están en otra unidad de traducción (aparte del hecho de que el compilador no puede saber si se está reordenando o no es un error). Por lo tanto, es razonable que el compilador advierta sobre cada instancia de orden no coincidente.

36

¿Por qué debería inicializar las variables de miembro en el orden en que están declaradas?

Los miembros se ser inicializado en el mismo orden en que se declaran, si desea o no. La advertencia le indica que el orden que está solicitando difiere del orden de ejecución real de la inicialización.

+3

+1 para una respuesta sucinta, al punto, que no usa la palabra "compilador". –

+1

Buena respuesta. ¿Podemos suponer que este es solo el estándar de C++ que asegura que todos los constructores se compilan de manera determinista, o hay alguna razón técnica/de rendimiento por la que los miembros siempre deben inicializarse en el orden en que se declaran? –

+4

@XavierHolt: En el lenguaje, para todos los objetos asignados dinámicamente, el orden de construcción y destrucción se invierte. Si el orden de inicialización fue determinado por la lista de inicializadores, entonces no se definiría el orden de inicialización en una clase con múltiples constructores y tampoco se definiría el orden de destrucción de los miembros. La razón por la que se invierte el orden de construcción/destrucción es garantizar que si un objeto depende de otro, el segundo no se destruirá antes que el primero. –

5

Me doy cuenta de que -Wall está pidiendo explícitamente a GCC que vaya por encima con avisos, pero supongo que hay una razón para todos ellos.

-Wall es solo un comienzo. Al contrario de lo que su nombre implica, -Wall no habilita todas las advertencias. Hay algunas advertencias que son posiblemente "exageradas", pero esas son precisamente las advertencias que -Wall no permite. Yo siempre uso -Wall más otros.

En cuanto a su reclamo, como ya han señalado otros, existe una muy buena razón para esta advertencia. El hecho de que especifique un pedido no significa que el compilador usará ese orden. El orden que el compilador debe usar según el estándar se basa en la definición de clase.

Cuestiones relacionadas