web-dev-qa-db-fra.com

Pourquoi devrais-je initialiser les variables membres dans l'ordre dans lequel elles sont déclarées?

J'écrivais du code aujourd'hui et j'ai eu une erreur de compilation étrange, qui semble être causée par l'initialisation des variables membres dans un ordre différent de celui déclaré.

Exemple:

class Test {
    int a;
    int b;

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

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

Ensuite, si je le compile avec -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

Je le réalise -Wall demande explicitement à GCC de passer outre les avertissements, mais je suppose qu'il y a une raison à tous. Alors, comment l'ordre d'initialisation des variables membres peut-il être important?

55
Brendan Long

La raison en est qu'ils sont initialisés dans l'ordre dans lequel ils sont déclarés dans votre classe, pas dans l'ordre dans lequel vous les avez initialisés dans le constructeur et cela vous avertit que l'ordre de votre constructeur ne sera pas utilisé.

Cela permet d'éviter les erreurs lorsque l'initialisation de b dépend de a ou vice-versa.

La raison de cet ordre est qu'il n'y a qu'un seul destructeur et qu'il doit choisir un "ordre inverse" pour détruire le membre de la classe. Dans ce cas, la solution la plus simple était d'utiliser l'ordre de déclaration dans la classe pour s'assurer que les attributs étaient toujours détruits dans le bon ordre inverse.

83
Mark B

Pourquoi devrais-je initialiser les variables membres dans l'ordre dans lequel elles sont déclarées?

Les membres seront seront initialisés dans le même ordre qu'ils sont déclarés, que vous le vouliez ou non. L'avertissement vous indique que l'ordre que vous demandez diffère de l'ordre réel d'exécution de l'initialisation.

Vous ne devriez pas, car cela diminue la lisibilité et est potentiellement trompeur.

Si vous l'avez fait:

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

il semblerait que b puis a soient tous deux définis sur 1, alors qu'en réalité la valeur non initialisée de b est utilisée pour initialiser a avant que b soit initialisé à 1.

32
CB Bailey

En fait, le compilateur toujours initialise les variables dans l'ordre de déclaration, même si vous écrivez les initialiseurs dans un ordre différent. Par conséquent, si vous n'écrivez pas les initialisations dans l'ordre de déclaration, l'ordre de vos initialiseurs ne correspond pas à l'ordre d'initialisation, ce qui peut conduire à des bogues subtils si les initialisations dépendent les unes des autres.

Par exemple, considérez le code

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

Ceci est un bogue car a est initialisé avant b, mais il ressemble OK. Si vous l'écrivez dans l'ordre de déclaration (qui est l'ordre d'initialisation), le bug devient évident:

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

Notez que le bogue peut également être plus subtil que cela; par exemple, imaginez que a et b sont des types de classe qui produisent quelque chose dans leur constructeur; puis avec l'ordre "incorrect", on pourrait penser que la sortie de b devrait apparaître avant celle de a alors qu'en réalité l'inverse se produira. Si la sortie de a qui apparaît en premier conduit à un fichier invalide, c'est aussi un bug, mais il n'y a aucun moyen que le compilateur puisse remarquer le problème si les constructeurs sont dans une autre unité de traduction (à part le fait que le compilateur ne peut pas savoir si la réorganisation est ou n'est pas un bug). Par conséquent, il est raisonnable que le compilateur avertisse simplement de chaque instance d'ordre non correspondant.

12
celtschk

Je me rends compte que -Wall demande explicitement à GCC d'aller au-delà des avertissements, mais je suppose qu'il y a une raison à tous.

-Le mur n'est qu'un début. Contrairement à ce que son nom l'indique, -Wall n'active pas tous les avertissements. Il y a certains avertissements qui sont sans doute "au-dessus", mais ce sont précisément les avertissements que -Wall n'active pas. J'utilise toujours -Wall ainsi que d'autres.

Quant à votre plainte, comme d'autres l'ont déjà noté, il y a une très bonne raison à cet avertissement. Ce n'est pas parce que vous spécifiez une commande que le compilateur utilisera cette commande. L'ordre que le compilateur doit utiliser conformément à la norme est basé sur la définition de classe.

5
David Hammen