Bonjour à tous,
je me demande comment je peux utiliser une liste d'initialisation avec plusieurs membres. Ma classe en possède plusieurs, et la FAQ ne montre un exemple qu'avec un seul...
Merci !
Version imprimable
Bonjour à tous,
je me demande comment je peux utiliser une liste d'initialisation avec plusieurs membres. Ma classe en possède plusieurs, et la FAQ ne montre un exemple qu'avec un seul...
Merci !
Avec une virgule .
Code:
1
2
3
4
5
6 A::A(T t,TT tt,TTT ttt):truc(t), titi(tt), toto(ttt) { }
Salut,
Tu les mets, tout simplement, l'un après l'autre, séparés par une virgule ;)
La seule chose à laquelle il faille faire attention, c'est de les mettre dans l'ordre dans lequel ils sont déclarés dans la classe ;)
Ouaip...
La preuve: si tu compile
tu auras un avertissement "a est initialisé apres f"...(enfin, avec les bons réglages du compilateur ;))Code:
1
2
3
4
5
6
7
8
9
10 class Maclass { public: Maclass(int a, float f):a(a),f(f){} ~Maclass(){} private: float f; int a; };
Cela n'a rien à voir avec l'ordre dans lequel sont fournis les arguments, mais c'est bel et bien l'ordre dans lequel sont déclarés, et donc construits, les membres en mémoire ;)
Voici le code utilisé :
Avec Visual C++ 8 et un avertissement niveau 4 (le max):Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 class Maclass { public: Maclass(int a, float f):a(a),f(f){} ~Maclass(){} private: float f; int a; }; int main() { Maclass t(2, 5.9f); return 0; }
Pas d'avertissements du tout ! :koi:Citation:
------ Début de la génération : Projet : test_MT, Configuration : Release Win32 ------
Compilation en cours...
main.cpp
Édition des liens en cours...
Génération de code en cours
Fin de la génération du code
Incorporation du manifeste en cours...
Le journal de génération a été enregistré à l'emplacement "file://c:\Documents and Settings\Gwen\Bureau\test_MT\test_MT\Release\BuildLog.htm"
test_MT - 0 erreur(s), 0 avertissement(s)
========== Génération : 1 a réussi, 0 a échoué, 0 mis à jour, 0 a été ignoré ==========
Une petite précision quand même...
Tu peux ne pas faire ta liste dans l'ordre de déclaration des membres, cela n'empêchera pas de compiler.
Par contre, tu te payeras les frais
1- de la création sans savoir fournir les valeurs
2- de l'"initialisation" au moment où on passe sur la variable adéquate (et peut etre meme une belle copie ou ne assignation)
Si par contre, tu crées ta liste d'initialisation dans l'ordre dans lequel les membres sont déclarés, tu peux directement faire appel au constructeur adéquat, sans copie ni assignation par la suite... Du coup, tu gagne du temps ;):D
Avec Codeblocks, il me signale bien ce souci...
Merci à vous en tout cas !
OK, comment veux tu que je fasse des progrès si le compilo m'indique même pas les warnings adéquats? :lol:
Bon c'est clair que Visual outrepasse légèrement la norme ! :cfou:
En plus tu dis qu'il peut y avoir une recopie lors de l'appel? 8O Ca craint pour la rapidité de mon code ! 8O
Je veux pas dire de bétise... mais il me semble bien que la norme est très claire sur le sujet:
Soit, de toute maniere, quelque soit l'ordre dans la liste d'initialisation ca ne change pas...Citation:
--Then, nonstatic data members shall be initialized in the order they
were declared in the class definition (again regardless of the order
of the mem-initializers).
Moi je dis que gcc devrait arrêter de fumer la moquette !
Par contre, il se peut que gcc affiche un warning (tout comme Visual d'ailleurs) si dans la liste d'initialisation, un constructeur utilise une variable déclarée APRES. Par exemple:
Pouff... warning... parceque l'initialisation se fait x, y, PUIS r ....Code:
1
2
3
4
5
6
7
8
9
10
11
12 class Toto { int x; int y; int r; Toto(); }; Toto::Toto() : x(45), r(30), y(r) {}
Les compilateurs les plus "hard" mettront même un warning dès qu'un membre de la classe est utilisé comme paramètre d'initialisation. Ceci dit, le comportement dans ce cas est clairement indiqué comme "non-défini".
Si Visual ne met pas de warning, c'est qu'il corrige tout seul le problème alors... Un tel compilateur ne peut pas se permettre des pertes de performances sans même en avertir l'utilisateur ! (dans ce cas au moins)
L'avertissement du compilo est juste pour nous prévenir qu'il va respecter l'ordre des déclarations des membres comme la norme lui demande.
Il n'est pas obligé de nous prévenir. C'est exactement pareil que les compilos qui signalent que 42 ne matchera pas un %s dans un printf.
Donc pas de recopie lors d'un appel ?
Visual est très mauvais en ce qui concerne les warnings. On l'a vu, même le 8 ne signale pas ce piège ("gotcha") pourtant courant, et en plus il ne possède aucun équivalent à -Wwrite-strings...
Je trouve au contraire que c'est une excellente chose que de prévenir du fait que quelque chose ne sera pas effectué de la manière dont la personne l'a codé, mais bel et bien de la manière dont la norme le suggère/précise...
Cela peut permettre au codeur de se rendre compte qu'il compte peut être un peu trop sur un effet de bord qui ne se présentera pas forcément, voire qu'il a suivi une mauvaise logique (une logique "illogique" :question:) et ne peut que l'inciter, s'il décide de prêter attention à ces avertissements, à coder des applications de meilleure qualité.
Rien n'est plus frustrant pour l'utilisateur de n'importe quelle application que de la voir "s'arrêter toute seule" parce que la (le groupe de) personne(s) qui l'a (l'ont) créé ont codé comme des cochons, et n'ont pas évité une erreur quelle qu'elle soit alors qu'il y avait moyen de l'éviter ;)
Mais c'est exactement ce que je dis: IL Y A un warning, quand c'est nécessaire !
Je viens de vérifier... VS2007 en tout cas met bien un warning dès que l'odre implique des effets de bord.
Si il n'y a aucun effet de bord, il n'y a pas de warning...
Je vais vérifier sous VS2005 dès que j'aurai le temps
Par exemple:
Ne met aucun warning...Code:
1
2
3
4
5
6
7
8 class A { int a; int b; int c; inline A(int p1, int p2) : c(p1), b(p2), a(0) {} };
Affiche un joli warning...Code:
1
2
3
4
5
6
7
8 class A { int a; int b; int c; inline A(int p1, int p2) : c(p1), b(p2), a(p1++) {} };
Je trouve ca tout à fait normal et cohérent avec la norme... Qui d'ailleurs, précise au passage comment l'initialisation doit être faite dans l'initialisation des membres non-statiques...
A noter que c'est exactement le même principe que pour l'initialisation des classes parentes:
Va commencer par initialiser C(2), PUIS B(0).... Les classes parentes étant initialisées dans l'ordre:Code:
1
2
3
4
5 class A : public C, B { inline A() : B(0), C(2) {} };
- classes virtuelles d'abord
- classes 'normales' dans l'ordre de profondeur, et de la déclaration (de gauche à droite donc).
- enfin les membres non-statiques dans l'ordre de leur déclaration.
Dans mon commentaire "hard", je voulais dire:
Si on suit la norme, ceci ne devrait pas mettre de warning... a étant correctement initialisé quand c l'est !Code:
1
2
3
4
5
6
7
8
9 class A { int a; int b; int c; inline A() : c(a++), b(4), a(2) {} };
Mais Visual affiche un warning dans ce cas là, SAUF si a est membre d'une classe parente. Ce qui est, à mon avis, TRES BIEN, car de toute manière c'est "laid" et surtout dangereux ;)
Pour etre sur que c'est clair, l'initialisation se fait dans l'ordre de declaration dans la classe (pourquoi? parce que la destruction se fait en sens inverse de construction et que le destructeur ne peut pas savoir -- sans cout supplementaire -- quel ordre a ete utilise, en particulier quand il y a plusieurs constructeurs) mais sans tes "frais".
La norme n'impose ni ne demande rien sur les warnings.Citation:
Envoyé par nicroman
Au contraire de toi, meme si l'application de la regle n'entraine pas un comportement different, je prefere avoir un warning: la situation n'est pas propre.
@nicroman
J'ai l'impression, à la lecture de ton raisonnement, que tu considère les avertissements comme des "empêcheurs de coder en rond"...
Il ne faut pas les voir ainsi:
Qu'il y ai ou non effet de bord, que ce soit dans une liste d'initialisation ou dans n'importe quelle autre situation dans laquelle on reçoit un avertissement du compilo, l'avertissement est là pour t'indiquer "attention, ton comportement est *potentiellement* dangereux/inutile/sans effet"...
Si un code est inutile/sans effet, cela signifie que tu alourdis inutilement ton code, et que tu peux supprimer un certain nombre de lignes... en gagnant en lisibilité.
Si un code est dangereux, et au moins tu es d'accord avec ca, c'est déjà pas si mal :P, cela signifie que tu es devant le précipice et que tu t'apprête à faire un grand pas en avant.
Que ce soit l'une ou l'autre des solutions, le fait de savoir que ta manière de coder ne sera pas prise en compte telle quelle ne peut qu'être bénéfique pour la qualité générale de ce que tu a codé...
Et comme le but d'un développeur reste, quand même, d'essayer de fournir les meilleures (ou en tout cas les moins mauvaises) applications possibles, je ne comprend pas vraiment pourquoi tu "râle" contre des avertissements que tu juges "inutiles"...
J'ai surtout l'impression que tu crois que je râle... :)
Un warning est un warning: "Ca m'empeche pas de compiler, mais je suis pas sur que ce que j'ai compris correspond bien a ce que tu veux faire".
C'est bien pour cette raison, qu'en général, on pousse les warnings au maximum, en leur faisant la chasse comme si c'était des erreurs (d'ailleurs on peut tout à fait dire au compilo, si warning, considere ca comme une erreur, ce qui devrait être la règle par défaut à mon avis).
Maintenant, trop de warnings poussent plus les programmeurs à baisser leur quantité par tweaking des options de compilation, plutôt que de les résoudre. Je me souviens d'un projet Eclipse par exemple, ou un mec avait viré les warning "Natural Language String" parce que dans son code ca lui faisait 300 warnings à chaque fichier...
Et certains compilateur mettront un warning sur une liste d'initialisation sans effet de bord (simple application des paramètres du constructeur par exemple), simplement parceque l'ordre n'est pas celui de déclaration, hors ce warning est complètement inutile ! Alors qu'il est absoluement nécessaire dès qu'il y a effet de bord possible (code appelé...) voire même qu'il soit carrément une erreur si il y a un comportement clairement indéfini !
Mais à ce moment là, il faut bien plus faire prendre conscience au programmeur qu'il est bien plus constructif de prendre un avertissement en compte que de diminuer le niveau d'avertissement...
Sinon, la loi de murphy nous indique que, s'il y a une c... à faire, on trouvera toujours quelqu'un pour la faire :P
Et comme les petits ruisseaux font les grands fleuves:
On commence par ne pas avertir que la liste d'initialisation sera pris dans l'ordre parce que c'est un comportement prévu par la norme
On continue en ne prévenant pas par un "1 est toujours vrai" quand on voit un code du genre de
(poussé à l'extrème, je te l'accorde :P)Code:if( 1 || fonction())
car, finalement, ce n'est qu'un comportement prévu par la norme :D
Sauf que, dans ce cas, fonction() est sensée modifier une valeur... mais elle n'est jamais appelée :P
Et au final, on obtient une application qui ne fait pas la moitié de ce qu'elle doit faire, ou qui le fait mal, ou qui ne réagit pas comme elle le devrait...
Alors, où arrêter le raisonnement du "c'est prévu par la norme" :question:
Autant se dire que, si le compilateur travaille différemment de ce que le codage laisse supposer, car les modifications à apporter ne sont, en définitive jamais si importantes que cela pour prendre les avertissements en compte, et s'assurer par ce simple fait que le programmeur aura pris conscience de son mauvais comportement...
Apres tout, un avertissement sur une liste d'initialisation gérée dans un autre ordre, ca nécessite quoi:question:
A peine le déplacement d'un terme, qui peut parfaitement être résolu par un couper/coller... Enorme comme travail, non 8O:nono::calim2:
Mais, au moins, on s'assure que si l'initialisation fait appel à un comportement à effet de bord, ce sera d'office pris en compte ;)
J'ai vaguement l'impression qu'on ne parle pas de la même chose....
La norme indique ce que doit faire le code généré par le compilateur....
Le compilateur essaye de traduire ce que veut faire le codeur.
1. Si il n'y a aucune ambiguité possible... par exemple:
Pourquoi mettre un warning ?Code:
1
2
3
4
5 class A { int a, b; A() : b(0), a(0) {} };
2. Si il y a ambiguité, mais "qui n'a pas l'air" de prêter à conséquence, le compilateur va faire un choix pour nous, et DOIT nous avertir que ceci est dangereux... par exemple:
On doit être rappelé que a va être construit avant b ! Le codeur, pour supprimer le warning, n'a qu'à inverser l'ordre de la liste d'initialisation...Code:
1
2
3
4
5 class A { int a,b; A(int n) : b(n++), a(n++) {} };
3. Si il y a un comportement indéfini, le compilateur DOIT pondre une erreur.
Code:
1
2
3
4
5 class A { int a,b; A(int n) : b(a++), a(n) {} };
Et ensuite ? tu préfères un warning dans le cas 1 ? Pourquoi ?
Pour éviter que si quelqu'un modifie le code plus tard il n'ai des problêmes ? Mais dans ce cas il tombera dans le cas 2 (warning) ou 3 (error) à la compilation... Je continue de ne pas voir l'interêt du warning pour le cas 1 !
Accroche toi à tes baskets, car je vais te faire la preuve par l'absurde du problème d'avertissements trop "ciblés" ;)
Je reprend tes deux codes:
Code:
1
2
3
4
5
6
7 class A { int a; int b; int c; inline A(int p1, int p2) : c(p1), b(p2), a(0) {}//aucun avertissement };
je modifie à peine le premier pour les assembler en un seulCode:
1
2
3
4
5
6
7 class A { int a; int b; int c; inline A(int p1, int p2) : c(p1), b(p2), a(p1++) {}//avertissement à cause de p1++ };
Maintenant, met toi à la place de celui qui fera la c... d'après la loi de Murphy:Code:
1
2
3
4
5
6
7
8
9
10 class A { int a; int b; int c; public: /* pas d'avertissement */ inline A(int p1, int p2, int p3) : c(p1), b(p2), a(p3) {} /* avertissement */ inline A(int p1, int p2) : c(p1), b(p2), a(p1++) {} };
Et, résultat des courses, il s'étonnera que dans le codeCitation:
Mais je ne comprend absolument pas pourquoi mon :furieux: de compilateur me sort un avertissement pour le deuxième constructeur et pas pour le premier alors que la liste d'initialisation est présentée exactement sous la même forme
:arrow::arrow: le compilateur ne sait pas ce qu'il dit ::arrow: je ne fais pas attention à ses avertissements (ou pire: je les désactive)
ne fournisse pas deux objets ayant des valeurs identiques...Code:
1
2
3 A obj1(4,3,5); A obj2(4,3);
Ce que je veux dire, en définitive, l'avantage de fournir un avertissement sur un cas qui "pourrait potentiellement poser problème" permet de se garder à meilleure distance des cas qui posent "réellement problème".
Si, dés le départ, tu "drille" le codeur à être attentif, dans sa liste d'initialisation, à l'ordre dans lequel il initialise les membres, même si, dans le contexte, cela ne pose aucun problème, il prendra l'habitude d'y être attentif.
Et donc, il finira par s'éloigner du fameux cas "peau de banane" d'une initialisation en utilisant une fonction à effet de bord...
Tout le monde sera alors gagnant :D