Voici un exemple simplifié pour illustrer la question:
class A {};
class B
{
B(A& a) : a(a) {}
A& a;
};
class C
{
C() : b(a) {}
A a;
B b;
};
Donc B est responsable de la mise à jour d'une partie de C. J'ai fait passer le code à travers lint et il s'est plaint du membre de référence: lint # 1725 . Cela parle de prendre soin de la copie et des affectations par défaut, ce qui est assez juste, mais la copie et l'affectation par défaut sont également mauvaises avec les pointeurs, donc il n'y a que peu d'avantages.
J'essaie toujours d'utiliser des références là où je peux, car les pointeurs nus introduisent une incertitude quant à savoir qui est responsable de la suppression de ce pointeur. Je préfère incorporer des objets par valeur, mais si j'ai besoin d'un pointeur, j'utilise auto_ptr dans les données membres de la classe qui possède le pointeur, et je passe l'objet comme référence.
Je n'utiliserais généralement un pointeur dans les données des membres que lorsque le pointeur pourrait être nul ou changer. Y a-t-il d'autres raisons de préférer les pointeurs aux références pour les membres de données?
Est-il vrai de dire qu'un objet contenant une référence ne doit pas être assignable, puisqu'une référence ne doit pas être modifiée une fois initialisée?
la source
Réponses:
Évitez les membres de référence, car ils restreignent ce que l'implémentation d'une classe peut faire (y compris, comme vous le mentionnez, empêcher l'implémentation d'un opérateur d'affectation) et n'apportent aucun avantage à ce que la classe peut fournir.
Exemples de problèmes:
jusqu'à C ++ 0x, de toute façonmodifier: C ++ a maintenant des constructeurs délégués ).
opérateur, etc.), mais se comporte comme un pointeur (peut pendre) - par exemple, Google Style Guide le découragela source
Ma propre règle de base:
la source
Les objets devraient rarement permettre d'assigner et d'autres choses comme la comparaison. Si vous considérez un modèle d'entreprise avec des objets tels que «Service», «Employé», «Directeur», il est difficile d'imaginer un cas où un employé sera affecté à un autre.
Ainsi, pour les objets métier, il est très utile de décrire les relations un-à-un et un-à-plusieurs comme des références et non des pointeurs.
Et il est probablement correct de décrire la relation un ou zéro comme un pointeur.
Donc pas de facteur «nous ne pouvons pas attribuer».
Beaucoup de programmeurs s'habituent simplement aux pointeurs et c'est pourquoi ils trouveront n'importe quel argument pour éviter l'utilisation de référence.
Avoir un pointeur en tant que membre vous forcera, vous ou un membre de votre équipe, à vérifier le pointeur encore et encore avant utilisation, avec un commentaire "juste au cas où". Si un pointeur peut être nul, le pointeur est probablement utilisé comme une sorte d'indicateur, ce qui est mauvais, car chaque objet doit jouer son propre rôle.
la source
Utilisez des références lorsque vous le pouvez et des pointeurs lorsque vous le devez.
la source
Dans quelques cas importants, la cessibilité n'est tout simplement pas nécessaire. Ce sont souvent des wrappers d'algorithmes légers qui facilitent le calcul sans quitter la portée. Ces objets sont des candidats de premier ordre pour les membres de référence, car vous pouvez être sûr qu'ils détiennent toujours une référence valide et n'ont jamais besoin d'être copiés.
Dans de tels cas, assurez-vous de rendre l'opérateur d'affectation (et souvent aussi le constructeur de copie) non utilisable (en héritant
boost::noncopyable
ou en les déclarant privé).Cependant, comme l'utilisateur pts l'a déjà commenté, il n'en va pas de même pour la plupart des autres objets. Ici, l'utilisation de membres de référence peut être un énorme problème et doit généralement être évitée.
la source
Comme tout le monde semble donner des règles générales, j'en proposerai deux:
N'utilisez jamais, jamais de références d'utilisation en tant que membres de la classe. Je ne l'ai jamais fait dans mon propre code (sauf pour me prouver que j'avais raison dans cette règle) et je ne peux pas imaginer un cas où je le ferais. La sémantique est trop confuse, et ce n'est vraiment pas pour cela que les références ont été conçues.
Toujours, toujours, utilisez des références lors du passage de paramètres aux fonctions, sauf pour les types de base, ou lorsque l'algorithme nécessite une copie.
Ces règles sont simples et m'ont bien servi. Je laisse faire des règles sur l'utilisation des pointeurs intelligents (mais s'il vous plaît, pas auto_ptr) en tant que membres de la classe aux autres.
la source
Oui à: Est-il vrai de dire qu'un objet contenant une référence ne doit pas être assignable, puisqu'une référence ne doit pas être modifiée une fois initialisée?
Mes règles de base pour les membres de données:
la source
Oui. Lisibilité de votre code. Un pointeur rend plus évident que le membre est une référence (ironiquement :)), et non un objet contenu, car lorsque vous l'utilisez, vous devez le dé-référencer. Je sais que certaines personnes pensent que c'est démodé, mais je pense toujours que cela évite simplement la confusion et les erreurs.
la source
Je déconseille aux membres de données de référence parce que vous ne savez jamais qui va dériver de votre classe et ce qu'ils pourraient vouloir faire. Ils peuvent ne pas vouloir utiliser l'objet référencé, mais étant une référence, vous les avez forcés à fournir un objet valide. Je me suis suffisamment fait cela pour arrêter d'utiliser des membres de données de référence.
la source
Si je comprends bien la question ...
Référence en tant que paramètre de fonction au lieu de pointeur: comme vous l'avez souligné, un pointeur n'indique pas clairement à qui appartient le nettoyage / l'initialisation du pointeur. Préférez un point partagé lorsque vous voulez un pointeur, c'est une partie de C ++ 11, et un low_ptr lorsque la validité des données n'est pas garantie pendant toute la durée de vie de la classe acceptant les données pointées. également une partie de C ++ 11. L'utilisation d'une référence comme paramètre de fonction garantit que la référence n'est pas nulle. Vous devez subvertir les fonctionnalités du langage pour contourner cela, et nous ne nous soucions pas des codeurs de canon lâches.
Référence une variable membre: comme ci-dessus en ce qui concerne la validité des données. Cela garantit que les données pointées, puis référencées, sont valides.
Le déplacement de la responsabilité de la validité de variable vers un point antérieur du code nettoie non seulement le code ultérieur (la classe A dans votre exemple), mais le rend également clair pour la personne qui utilise.
Dans votre exemple, qui est un peu déroutant (j'essaierais vraiment de trouver une implémentation plus claire), le A utilisé par B est garanti pour la durée de vie du B, puisque B est membre de A, donc une référence le renforce et est (peut-être) plus clair.
Et juste au cas où (ce qui est peu probable car cela n'aurait aucun sens dans le contexte de vos codes), une autre alternative, utiliser un paramètre A non référencé et non pointeur, copierait A et rendrait le paradigme inutile - je ne pensez vraiment pas que vous entendez cela comme une alternative.
En outre, cela garantit également que vous ne pouvez pas modifier les données référencées, où un pointeur peut être modifié. Un pointeur const ne fonctionnerait que si le pointeur référencé / vers les données n'était pas modifiable.
Les pointeurs sont utiles s'il n'est pas garanti que le paramètre A pour B soit défini ou s'il peut être réaffecté. Et parfois, un pointeur faible est tout simplement trop détaillé pour l'implémentation, et un bon nombre de personnes ne savent pas ce qu'est un low_ptr ou ne les aiment tout simplement pas.
Déchirez cette réponse, s'il vous plaît :)
la source