2010-10-08 6 views
2

J'ai eu du mal à déboguer un crash sur la production. Je voulais juste confirmer avec les gens ici la sémantique. Nous avons une classe comme ...Ctor Initializer: l'auto-initialisation provoque un crash?

class Test { 
public: 
    Test() 
    { 
    // members initialized ... 
    m_str = m_str; 
    } 
    ~Test() {} 
private: 
    // other members ... 
    std::string m_str; 
}; 

Quelqu'un a modifié l'initialisation d'utiliser des listes d'initialisation-ctor qui est raisonnablement correct dans notre sémantique de code. L'ordre d'initialisation et leur valeur initiale est correcte entre autres. Donc la classe ressemble à ...

class Test { 
public: 
    Test() 
    : /*other inits ,,, */ m_str(m_str) 
    { 
    } 
    ~Test() {} 
private: 
    // other members ... 
    std::string m_str; 
}; 

Mais le code a soudainement commencé à planter! J'ai isolé la longue liste d'inits à ce morceau de code m_str(m_str). Je l'ai confirmé via link text.

Faut-il tomber en panne? Que dit la norme à ce sujet? (Est-ce un comportement indéfini?)

+4

Vous initialisez m_str avec lui-même? Pourquoi fais-tu ça? – Starkey

+1

Un comportement indéfini signifie qu'il peut formater votre disque c et installer un autre système d'exploitation. Personne ne sait quel "comportement non défini" signifie sur un environnement particulier. –

+0

le code de quelqu'un d'autre, je vais le changer, mais j'ai besoin de la confirmation de la norme qu'il est en effet un comportement non défini, parce qu'il ne plante pas pour les types de POD comme 'int i; i (i) 'dans la liste des initialiseurs – Abhay

Répondre

13

Le premier constructeur est équivalent à

Test() 
    : m_str() 
    { 
    // members initialized ... 
    m_str = m_str; 
    } 

qui est, au moment où vous arrivez à la mission dans le constructeur, m_stra déjà été initialisé implicitement à une chaîne vide. Ainsi, l'affectation à soi-même, bien que totalement dénuée de sens et superflu, ne pose aucun problème (puisque std::string::operator=(), comme tout opérateur d'assignation bien écrit devrait, vérifie l'auto-affectation et ne fait rien dans ce cas).

Cependant, dans le deuxième constructeur, vous essayez d'initialiser m_str avec lui-même dans la liste des initialiseur - à quel point il est pas encore initialisé. Le résultat est donc un comportement indéfini.

Mise à jour: Pour les types primitifs, cela est encore un comportement non défini (résultant dans un champ avec la valeur des déchets), mais il ne tombe pas en panne (en général - voir les commentaires ci-dessous pour les exceptions) parce que les types primitifs est, par définition constructeurs , destructeurs et ne contiennent aucun pointeur vers d'autres objets.

La même chose est vraie pour tout type qui ne contient pas de membres de pointeur avec la sémantique de propriété. std::string est démontré comme n'étant pas l'un de ceux-ci :-)

+2

Dépend de ce que vous entendez par "types primitifs". Même l'attribution d'une valeur indéterminée à 'int' peut en principe provoquer un plantage, car la norme autorise la représentation de remplissage et d'interruption pour' int'. Vous devez descendre aux types 'char' pour avoir du code portable avec une initialisation de soi bien définie. –

+0

@Alf même pour les types 'char', tel est le comportement indéfini. Seulement si vous déréférencer un pointeur de type 'char' il est défini pour tous les motifs possibles (car il lit ensuite à partir de la mémoire et parce que les types' char' n'ont pas de pièges). Voir la dernière note à http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#240, * "Le CWG ne considérait pas que l'accès à un char non signé pourrait encore être piégé s'il est dans un registre et doit réévaluer la résolution proposée sous cet angle. "*. –

+0

@Johannes: merci, je n'ai pas pensé à ça! (Mais le comité ne l'a pas non plus ...) Cependant, je suis sûr qu'il s'agit du dernier souffle de «compatibilité ENIAC», et qu'il n'y a pas de problème de pratique - du moins, je l'espère sincèrement! :-) –

2

m_str est construit dans la liste d'initialisation. Par conséquent, au moment où vous l'attribuez à lui-même, il n'est pas entièrement construit. Par conséquent, un comportement indéfini.

(Qu'est-ce que l'auto-affectation censé faire de toute façon?)

+0

Je donno, le code de quelqu'un d'autre. J'ai besoin de confirmation si c'est un comportement non défini. Becoz ne tombe définitivement pas sur les types POD. – Abhay

+4

pourquoi avez-vous besoin de confirmation? C'est un mauvais code. Juste le réparer. – Tim

+0

@Tim: D'accord. Mais son code direct, frappé un million de fois par jour. J'ai besoin de l'approbation de ppl plus haut qui dit 'ça a fonctionné depuis des lustres ...' J'ai donc besoin d'une référence convaincante. – Abhay

1

comportement non défini ne doit pas conduire à un accident - il peut faire à peu près tout, de continuer à travailler comme s'il n'y avait pas problème du tout, de s'écraser immédiatement, de faire quelque chose de vraiment étrange qui provoque des problèmes apparemment sans rapport plus tard. La revendication canonique est que cela fait "voler les démons de votre nez" (aka, "provoque des démons nasaux"). À un moment donné, l'inventeur de la phase avait un site Web (plutôt cool) racontant la guerre nucléaire qui a commencé à partir de quelqu'un causant un comportement indéfini dans le "DeathStation 9000".

Edit: Le libellé exact de la norme est (§: 1.3.12):

1.3.12 comportement non défini [defns.undefined]

comportement, comme pourraient résulter d'une utilisation de une construction de programme erronée ou des données erronées, pour lesquelles cette Norme internationale n'impose aucune exigence. Un comportement indéfini peut également être attendu lorsque cette norme internationale omet la description de toute définition explicite du comportement. [Note: le comportement non défini va de l'ignorance complète de la situation avec des résultats imprévisibles à la traduction ou à l'exécution de programme de façon documentée caractéristique de l'environnement (avec ou sans l'émission d'un message de diagnostic), à la fin d'une traduction ou exécution (avec l'émission d'un message de diagnostic ).

0

Ceci est la même différence entre

std::string str; 
str = str; 

et

std::string str(str); 

Les travaux antérieurs (bien que ce soit un non-sens), ce dernier n'a pas, car il essaie de copier-construction un objet d'un objet non encore construit.

Bien sûr, la voie à suivre serait

Test() : m_str() {} 
2

L'initialisation d'origine par affectation est complètement superflu.

Cela n'a causé aucun dommage, hormis le gaspillage des cycles du processeur, car au moment de l'affectation, le membre m_str avait déjà été initialisé, par défaut.

Dans le deuxième extrait de code, l'initialisation par défaut est substituée pour utiliser le membre non encore initialisé pour s'initialiser. C'est un comportement indéfini. Et c'est complètement inutile: il suffit de supprimer cela (et ne pas réintroduire le time-waster d'origine, juste, supprimer).

En augmentant le niveau d'avertissement de votre compilateur, vous pourriez être en mesure d'obtenir des avertissements à propos de ce code trivial trivial similaire.

Malheureusement, le problème que vous rencontrez n'est pas technique, c'est beaucoup plus fondamental. C'est comme si un travailleur dans une usine automobile posait une question sur les roues carrées qu'ils mettent sur la nouvelle marque de voiture. Ensuite, le problème n'est pas que les roues carrées ne fonctionnent pas, c'est que beaucoup d'ingénieurs et de managers ont été impliqués dans la décision d'utiliser les jantes carrées fantaisie et aucun d'entre eux ne s'y est opposé - certains d'entre eux ne l'ont pas fait Je comprends que les roues carrées ne fonctionnent pas, mais la plupart d'entre elles, je suppose, avaient simplement peur de dire de quoi elles étaient sûres à 100%. C'est donc probablement un problème de gestion. Je suis désolé, mais je ne connais pas de solution pour cela ...

+0

Vrai dans une certaine mesure dans mon cas aussi bien. Je pensais pouvoir les convaincre en utilisant la norme C++ comme référence. Donc en attente de la citation .. Je n'ai pas une copie de cela/Internet pour n3092.pdf soit – Abhay

+0

Vous pouvez télécharger la dernière version de la norme à partir des pages du comité C++, à [http://www.open-std.org/jtc1/sc22/wg21 /]. –