64 votes

Est-il sûr d'utiliser le pointeur "this" dans une liste d'initialisation ?

J'ai deux classes avec une relation parent-enfant (la classe Parent classe "has-a" Child ), et la Child a un pointeur vers la classe Parent . Il serait bien d'initialiser le pointeur du parent lors de la construction de l'enfant, comme suit :

class Child;
class Parent;

class Child
{
public:
 Child (Parent* parent_ptr_) : parent_ptr(parent_ptr_) {};

private:
 Parent* parent_ptr;
};

class Parent
{
public:
    Parent() : child(this) {};

private:
    Child child;
}

Maintenant, je sais que les gens recommandent de ne pas utiliser this dans la liste d'initialisation, et FAQ C++ dit que je vais avoir un avertissement du compilateur (BTW, sous VS2010, je n'ai pas d'avertissement), mais j'aime vraiment mieux cela que d'appeler une fonction set dans Parent Le constructeur de l'entreprise. Mes questions sont les suivantes :

  • Est-ce que le parent this bien défini lorsque le Child est en train d'être créé ?
  • Si oui, pourquoi est-il considéré comme une mauvaise pratique de l'utiliser comme ci-dessus ?

Merci,

Boaz

EDIT : Merci Timbo, c'est en effet un duplicate (hein, j'ai même choisi les mêmes noms de classe). Alors, apportons une valeur ajoutée : qu'en est-il des références ? Est-il possible / sûr de faire ce qui suit ? :

class Child
{
public:
 Child (Parent& parnet_ptr_) : parent_ptr(parent_ptr_) {};

private:
 Parent* parent_ptr;
};

class Parent
{
public:
    Parent() : child(*this) {};

private:
    Child child;
}

0 votes

0 votes

@thecoshman - jeu de mots ?

0 votes

Vs2010 produit effectivement un avertissement à ce sujet, cela dépend du niveau d'avertissement qui a été défini.

65voto

Nawaz Points 148870

Oui. Il est sûr à utiliser this pointeur dans la liste d'initialisation tant qu'il n'est pas utilisé pour accéder à des membres non initialisés ou à des fonctions virtuelles, directement ou indirectement. car l'objet n'est pas encore totalement construit. L'objet child peut stocker le this pointeur de Parent pour une utilisation ultérieure !

2 votes

Il est également dangereux d'appeler des fonctions virtuelles dans le parent avant que la construction de ce dernier ne soit terminée.

4 votes

En bref. L'enfant NE PEUT PAS utiliser ce pointeur dans son constructeur/destructeur mais sinon c'est OK.

0 votes

Cela signifie-t-il qu'il est possible de l'utiliser pour accéder à des membres déjà initialisés ? Par exemple struct foo { int x,y; foo(int z) : x(z), y(this->x) {} }; ou pourquoi ne pas utiliser this pour faire référence au membre comme dans ce cas : struct foo { int x; foo(int z) : this->x(z) {} }; ?

14voto

Matteo Italia Points 53117

Le parent this pointeur, en "termes de pointeur", est bien défini (sinon comment le constructeur parent saurait-il sur quelle instance il opère ?), mais :

  • les champs qui sont déclarés après le Child ne sont pas encore initialisés ;
  • le code dans le constructeur n'a pas encore été exécuté ;
  • De plus, les avertissements habituels concernant l'utilisation de membres virtuels depuis le constructeur s'appliquent. 1 .

Ainsi, l'objet parent en général est toujours dans un état incohérent ; tout ce que l'objet enfant fera lors de la construction sur l'objet parent, sera fait sur un objet à moitié construit, et ce n'est en général pas une bonne chose (par exemple, s'il appelle des méthodes "normales" - qui reposent sur le fait que l'objet est entièrement construit - vous pouvez vous retrouver dans des chemins de code "impossibles").

Néanmoins, si tout ce que l'objet enfant fait avec le pointeur parent dans son constructeur est de le stocker pour l'utiliser plus tard (=> quand il sera réellement construit), il n'y a rien de mal à cela.


  1. C'est-à-dire que la répartition virtuelle ne fonctionne pas dans les constructeurs, parce que la vtable n'a pas encore été mise à jour par le constructeur de la classe dérivée. Voir par exemple ici .

0 votes

@Matteo Italia : +1. Au fait, pourquoi avez-vous mis le troisième point entre parenthèses ? Il est aussi important que les autres. Enlevez les parenthèses !

0 votes

@Nawaz : merci, parenthèses enlevées (BTW, vous aussi avez eu mon +1 avant que je commence à écrire :) ). Les parenthèses étaient là parce que ce n'était pas un avertissement. spécifique à ce scénario, mais général sur les fonctions virtuelles et les constructeurs ; c'était un conseil "juste au cas où vous ne l'auriez pas retenu". :)

0 votes

Question : est-ce l'ordre des déclarations de champs ou l'ordre des initialisateurs qui est significatif ? Je pensais à ce dernier (lorsqu'ils sont présents), mais cela fait un moment que je ne m'en suis pas préoccupé.

5voto

Oli Charlesworth Points 148744

Le comportement est bien défini tant que vous ne tentez pas de déréférencer le pointeur jusqu'à ce qu'il ait été déréférencé. après le site Parent a été complètement construit (comme @Sergey le dit dans un commentaire ci-dessous, si l'objet en cours de construction est en fait dérivé de l'objet Parent alors tous de ses constructeurs doivent avoir terminé).

1 votes

Pas exactement le constructeur Parent. Strictement parlant, l'accès libre au pointeur n'est garanti que lorsque tous les constructeurs de l'objet ont terminé. Dans le constructeur Parent, nous n'avons aucun moyen de savoir si c'est réellement un Parent qui est construit ou si c'est une sous-classe éloignée de celui-ci qui a ses champs et qui réimplémente éventuellement certaines fonctions virtuelles définies dans le Parent.

0 votes

@Sergey : C'est un très bon point. Je vais mettre à jour ma réponse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X