2009-12-01 10 views
117

La opción g ++ -Wall incluye -Wreorder. Lo que hace esta opción se describe a continuación. No es obvio para mí por qué a alguien le importaría (especialmente lo suficiente para activar esto por defecto en -Wall).¿Cuál es el punto de g ++ -Wreorder?

 
-Wreorder (C++ only) 
    Warn when the order of member initializers given in the code does not 
    match the order in which they must be executed. For instance: 

    struct A { 
     int i; 
     int j; 
     A(): j (0), i (1) { } 
    }; 

    The compiler will rearrange the member initializers for i and j to 
    match the declaration order of the members, emit-ting a warning to that 
    effect. This warning is enabled by -Wall. 

Respuesta

195

considerar:

struct A { 
    int i; 
    int j; 
    A() : j(0), i(j) { } 
}; 

Ahora i se inicializa a un valor desconocido, no es cero.

Alternativamente, la inicialización de i puede tener algunos efectos secundarios para los cuales el orden es importante. P.ej.

A(int n) : j(n++), i(n++) { } 
+60

Esto realmente debería ser el ejemplo en la documentación. –

+3

gracias. Dado que la mayoría de nuestros tipos son tipos de POD con inicializadores simples, esto no se me ocurrió. Su ejemplo es mucho mejor que el ejemplo del manual de g ++. –

+0

El ejemplo en esta es la respuesta es terriblemente confuso. Cuando probé esto, encontré que 'i' estaba ** inicializado consistentemente ** a' 0', lo que hace que parezca que nada es erróneo. Pero si usas algo como '100', entonces ves que' j' es '100' y' i' es '0'. Sé que probablemente se deba al compilador, pero estoy usando g ++ por defecto que obtengo con Ubuntu, por lo que debería ser una ocurrencia común. –

9

Esto puede afectarle si sus inicializadores tienen efectos secundarios. Consideremos:

int foo() { 
    puts("foo"); 
    return 1; 
} 

int bar() { 
    puts("bar"); 
    return 2; 
} 

struct baz { 
    int x, y; 
    baz() : y(foo()), x(bar()) {} 
}; 

Lo anterior imprimir "barra" y luego "foo", a pesar de que intuitivamente es de suponer que el orden es como está escrito en la lista de inicialización.

Alternativamente, si x y y son de algún tipo definido por el usuario con un constructor, ese constructor también puede tener efectos secundarios, con el mismo resultado no obvio.

También se puede manifestar cuando el inicializador de un miembro hace referencia a otro miembro.

29

El problema es que alguien podría ver la lista de iniciadores de miembros en el constructor, y pensar que se ejecutan en ese orden (j primero, luego i). No lo son, se ejecutan en el orden en que los miembros se definen en la clase.

Supongamos que ha escrito A(): j(0), i(j) {}. Alguien podría leer eso, y pensar que termino con el valor 0. No, porque lo inicializó con j, que contiene basura porque no se ha inicializado.

La advertencia le recuerda que debe escribir A(): i(j), j(0) {}, que con suerte se ve mucho más sospechoso.

+0

¡Parece/huele a pescado de hecho! :) Definitivamente código olor :) Gracias por su explicación clara que va directo al grano. :) – Will

+0

Bien explicado, ¡esta debería ser la mejor respuesta! – Aaron

+0

"... le recuerda que escriba A(): i (j), j (0) {} ..." Sugiero que le recuerde que debe volver a ordenar los miembros de la clase en este caso particular. –

5

La advertencia existe porque si acaba de leer el constructor, parece que j se está inicializando antes de i. Esto se convierte en un problema si uno se utiliza para inicializar la otra, como en

struct A { 
    int i; 
    int j; 
    A(): j (0), i (this->j) { } 
}; 

Cuando sólo se observa en el constructor, esto se ve seguro. Pero en realidad, j aún no se ha inicializado en el punto donde se utiliza para inicializar i, por lo que el código no funcionará como se esperaba. De ahí la advertencia.

9

Otras respuestas han proporcionado algunos buenos ejemplos que justifican la opción de advertencia. Pensé que proporcionaría un contexto histórico. El creador de C++, Bjarne Stroustrup, explica en su libro The C++ programming language (3ª edición, página 259):

Los miembros constructores son llamados antes de que el cuerpo de la clase que contiene se ejecuta propio constructor. Los constructores se llaman en el orden en que se declaran en la clase en lugar del orden en el que aparecen en la lista de inicializadores. Para evitar confusiones, es mejor especificar los inicializadores en orden de declaración.Los destructores miembros se llaman en el orden inverso de construcción.