web-dev-qa-db-fra.com

Un membre doit-il être initialisé pour prendre son adresse?

Puis-je initialiser un pointeur sur un membre de données avant d'initialiser le membre? En d'autres termes, est-ce un C++ valide?

#include <string>

class Klass {
public:
    Klass()
        : ptr_str{&str}
        , str{}
    {}
private:
    std::string *ptr_str;
    std::string str;
};

cette question est similaire à la mienne, mais l'ordre est correct là-bas, et la réponse dit

Je déconseille le codage comme celui-ci au cas où quelqu'un changerait l'ordre des membres de votre classe.

Ce qui semble signifier qu'il serait illégal d'annuler l'ordre, mais je n'en suis pas sûr.

23
Ayxan

Un membre doit-il être initialisé pour prendre son adresse?

Non.

Puis-je initialiser un pointeur sur un membre de données avant d'initialiser le membre? En d'autres termes, est-ce un C++ valide?

Oui. Oui.

Il n'y a aucune restriction que l'opérande d'unaire et doit être initialisé. Il y a un exemple dans la norme de spécification d'unaire et d'opérateur:

int a;
int* p1 = &a;

Ici, la valeur de a n'est pas initialisée et il est correct de la pointer.

Ce que cet exemple ne démontre pas pointe vers un objet avant que sa durée de vie ne commence, ce qui se passe dans votre exemple. tilisation un pointeur sur un objet avant et après sa durée de vie est explicitement autorisé si le stockage est occupé. Le projet standard dit:

[basic.life] Avant le début de la durée de vie d'un objet, mais après que le stockage que l'objet occupera a été alloué ou, après la fin de la durée de vie d'un objet et avant que le stockage que l'objet occupé est réutilisé ou libéré, tout pointeur qui représente l'adresse du lieu de stockage où l'objet sera ou était situé peut être utilisé mais uniquement de manière limitée ...

La règle indique ensuite comment l'utilisation est restreinte. Vous pouvez vous débrouiller avec bon sens. En bref, vous pouvez le traiter comme vous pourriez traiter un void*, sauf que violer ces restrictions est UB plutôt que mal formé. Une règle similaire existe pour les références.

Il existe également des restrictions sur le calcul spécifique de l'adresse des membres non statiques. Le projet standard dit:

[class.cdtor] ... Pour former un pointeur vers (ou accéder à la valeur de) un membre direct non statique d'un objet obj, la construction de obj doit avoir commencé et son la destruction ne doit pas être terminée, sinon le calcul de la valeur du pointeur (ou l'accès à la valeur du membre) entraîne un comportement indéfini.

Dans le constructeur de Klass, la construction de Klass a commencé et la destruction n'est pas terminée, donc la règle ci-dessus est satisfaite.


P.S. Votre classe est copiable, mais la copie aura un pointeur vers le membre d'une autre instance. Demandez-vous si cela a du sens pour votre classe. Sinon, vous devrez implémenter des constructeurs et des opérateurs d'affectation et de copie personnalisés. Une auto-référence comme celle-ci est un cas rare où vous pouvez avoir besoin de définitions personnalisées pour celles-ci, mais pas d'un destructeur personnalisé, c'est donc une exception à la règle de cinq (ou trois).

P.P.Si votre intention est de pointer vers l'un des membres et aucun objet autre qu'un membre, vous pouvez utiliser un pointeur sur membre au lieu d'un pointeur sur objet.

25
eerorika