si vous avez besoin de renvoyer un pointeur - mieux utiliser une valeur de retour
littleadv
6
Pouvez-vous expliquer pourquoi? Cette recommandation n'est pas très utile. Ma question est tout à fait légitime. Ceci est actuellement utilisé dans le code de production. Je ne comprends tout simplement pas pourquoi.
Matthew Hoggan
1
David résume assez bien la situation. Le pointeur lui-même est en cours de modification.
chris
2
Je passe par là si j'appelle l'opérateur "Nouveau" dans la fonction.
EMI du
Réponses:
142
Vous souhaiterez passer un pointeur par référence si vous avez besoin de modifier le pointeur plutôt que l'objet vers lequel le pointeur pointe.
Ceci est similaire à la raison pour laquelle des pointeurs doubles sont utilisés; l'utilisation d'une référence à un pointeur est légèrement plus sûre que l'utilisation de pointeurs.
Donc similaire à un pointeur d'un pointeur, sauf un niveau d'indirection de moins pour la sémantique passe-par-référence?
Je voudrais ajouter que parfois, vous souhaitez également renvoyer un pointeur par référence. Par exemple, si vous avez une classe contenant des données dans un tableau alloué dynamiquement, mais que vous souhaitez fournir un accès (non constant) à ces données au client. Dans le même temps, vous ne voulez pas que le client puisse manipuler la mémoire via le pointeur.
2
@William pouvez-vous élaborer là-dessus? Ça semble intéressant. Cela signifie-t-il que l'utilisateur de cette classe pourrait obtenir un pointeur vers le tampon de données interne et y lire / écrire, mais il ne peut pas - par exemple - libérer ce tampon en utilisant deleteou relier ce pointeur à un autre endroit de la mémoire? Ou est-ce que je me suis trompé?
BarbaraKwarc
2
@BarbaraKwarc Bien sûr. Supposons que vous ayez une implémentation simple d'un tableau dynamique. Naturellement, vous surchargerez l' []opérateur. Une signature possible (et en fait naturelle) pour cela serait const T &operator[](size_t index) const. Mais vous pourriez aussi avoir T &operator[](size_t index). Vous pourriez avoir les deux en même temps. Ce dernier vous permettrait de faire des choses comme myArray[jj] = 42. En même temps, vous ne fournissez pas de pointeur vers vos données, de sorte que l'appelant ne peut pas jouer avec la mémoire (par exemple, supprimez-la accidentellement).
Oh. Je pensais que vous parliez de renvoyer une référence à un pointeur (votre libellé exact: "renvoyer un pointeur par référence", parce que c'était ce à quoi OP demandait: passer des pointeurs par des références, pas simplement de vieilles références) comme une façon élégante de donner un accès en lecture / écriture au tampon sans permettre de manipuler le tampon lui-même (par exemple en le libérant). Renvoyer une simple référence ancienne est une chose différente et je le sais.
BarbaraKwarc
65
50% des programmeurs C ++ aiment définir leurs pointeurs sur null après une suppression:
template<typename T>void moronic_delete(T*& p){delete p;
p =nullptr;}
Sans la référence, vous ne modifieriez qu'une copie locale du pointeur, sans affecter l'appelant.
J'échangerai le son stupide pour trouver la réponse: pourquoi est-ce que cela s'appelle supprimer stupide? Qu'est-ce que je rate?
Sammaron
1
@Sammaron Si vous regardez l'historique, le modèle de fonction était appelé paranoid_delete, mais Puppy l'a renommé moronic_delete. Il appartient probablement aux 50% restants;) Quoi qu'il en soit, la réponse courte est que les paramètres pointeurs vers null après ne deletesont presque jamais utiles (car ils devraient sortir de la portée, de toute façon) et gênent souvent la détection de bogues "use after free".
fredoverflow
@fredoverflow Je comprends votre réponse, mais @Puppy semble penser que deletec'est stupide en général. Chiot, j'ai fait quelques recherches sur Google, je ne vois pas pourquoi la suppression est complètement inutile (peut-être que je suis aussi un terrible googleur;)). Pouvez-vous expliquer un peu plus ou fournir un lien?
Sammaron
@Sammaron, C ++ a maintenant des "pointeurs intelligents" qui encapsulent des pointeurs réguliers et appellent automatiquement "delete" pour vous lorsqu'ils sont hors de portée. Les pointeurs intelligents sont largement préférés aux pointeurs stupides de style C car ils éliminent complètement ce problème.
tjwrona1992
14
La réponse de David est correcte, mais si elle est encore un peu abstraite, voici deux exemples:
Vous souhaiterez peut-être remettre à zéro tous les pointeurs libérés pour détecter les problèmes de mémoire plus tôt. C-style que vous feriez:
Lorsqu'il s'agit de listes chaînées - souvent simplement représentées comme des pointeurs vers un nœud suivant:
structNode{value_t value;Node* next;};
Dans ce cas, lorsque vous insérez dans la liste vide, vous devez nécessairement changer le pointeur entrant car le résultat n'est plus le NULLpointeur. C'est un cas où vous modifiez un pointeur externe à partir d'une fonction, afin qu'il ait une référence au pointeur dans sa signature:
J'ai dû utiliser un code comme celui-ci pour fournir des fonctions permettant d'allouer de la mémoire à un pointeur passé et de renvoyer sa taille parce que ma société "m'objecte" en utilisant la STL
int iSizeOfArray(int*&piArray){
piArray =newint[iNumberOfElements];...return iNumberOfElements;}
Ce n'est pas sympa, mais le pointeur doit être passé par référence (ou utiliser un double pointeur). Sinon, la mémoire est allouée à une copie locale du pointeur si elle est passée par valeur, ce qui entraîne une fuite de mémoire.
Un exemple est lorsque vous écrivez une fonction d'analyseur et lui passez un pointeur source à lire, si la fonction est censée pousser ce pointeur en avant derrière le dernier caractère qui a été correctement reconnu par l'analyseur. L'utilisation d'une référence à un pointeur indique clairement que la fonction déplacera le pointeur d'origine pour mettre à jour sa position.
En général, vous utilisez des références à des pointeurs si vous souhaitez transmettre un pointeur à une fonction et le laisser déplacer ce pointeur d' origine vers une autre position au lieu de simplement en déplacer une copie sans affecter l'original.
Pourquoi le vote négatif? Y a-t-il quelque chose qui ne va pas avec ce que j'ai écrit? Soin d'expliquer? : P
BarbaraKwarc
0
Une autre situation où vous pourriez en avoir besoin est si vous avez une collection stl de pointeurs et que vous souhaitez les modifier à l'aide de l'algorithme stl. Exemple de for_each en c ++ 98.
Réponses:
Vous souhaiterez passer un pointeur par référence si vous avez besoin de modifier le pointeur plutôt que l'objet vers lequel le pointeur pointe.
Ceci est similaire à la raison pour laquelle des pointeurs doubles sont utilisés; l'utilisation d'une référence à un pointeur est légèrement plus sûre que l'utilisation de pointeurs.
la source
delete
ou relier ce pointeur à un autre endroit de la mémoire? Ou est-ce que je me suis trompé?[]
opérateur. Une signature possible (et en fait naturelle) pour cela seraitconst T &operator[](size_t index) const
. Mais vous pourriez aussi avoirT &operator[](size_t index)
. Vous pourriez avoir les deux en même temps. Ce dernier vous permettrait de faire des choses commemyArray[jj] = 42
. En même temps, vous ne fournissez pas de pointeur vers vos données, de sorte que l'appelant ne peut pas jouer avec la mémoire (par exemple, supprimez-la accidentellement).50% des programmeurs C ++ aiment définir leurs pointeurs sur null après une suppression:
Sans la référence, vous ne modifieriez qu'une copie locale du pointeur, sans affecter l'appelant.
la source
paranoid_delete
, mais Puppy l'a renommémoronic_delete
. Il appartient probablement aux 50% restants;) Quoi qu'il en soit, la réponse courte est que les paramètres pointeurs vers null après nedelete
sont presque jamais utiles (car ils devraient sortir de la portée, de toute façon) et gênent souvent la détection de bogues "use after free".delete
c'est stupide en général. Chiot, j'ai fait quelques recherches sur Google, je ne vois pas pourquoi la suppression est complètement inutile (peut-être que je suis aussi un terrible googleur;)). Pouvez-vous expliquer un peu plus ou fournir un lien?La réponse de David est correcte, mais si elle est encore un peu abstraite, voici deux exemples:
Vous souhaiterez peut-être remettre à zéro tous les pointeurs libérés pour détecter les problèmes de mémoire plus tôt. C-style que vous feriez:
En C ++ pour faire de même, vous pouvez faire:
Lorsqu'il s'agit de listes chaînées - souvent simplement représentées comme des pointeurs vers un nœud suivant:
Dans ce cas, lorsque vous insérez dans la liste vide, vous devez nécessairement changer le pointeur entrant car le résultat n'est plus le
NULL
pointeur. C'est un cas où vous modifiez un pointeur externe à partir d'une fonction, afin qu'il ait une référence au pointeur dans sa signature:Il y a un exemple dans cette question .
la source
J'ai dû utiliser un code comme celui-ci pour fournir des fonctions permettant d'allouer de la mémoire à un pointeur passé et de renvoyer sa taille parce que ma société "m'objecte" en utilisant la STL
Ce n'est pas sympa, mais le pointeur doit être passé par référence (ou utiliser un double pointeur). Sinon, la mémoire est allouée à une copie locale du pointeur si elle est passée par valeur, ce qui entraîne une fuite de mémoire.
la source
Un exemple est lorsque vous écrivez une fonction d'analyseur et lui passez un pointeur source à lire, si la fonction est censée pousser ce pointeur en avant derrière le dernier caractère qui a été correctement reconnu par l'analyseur. L'utilisation d'une référence à un pointeur indique clairement que la fonction déplacera le pointeur d'origine pour mettre à jour sa position.
En général, vous utilisez des références à des pointeurs si vous souhaitez transmettre un pointeur à une fonction et le laisser déplacer ce pointeur d' origine vers une autre position au lieu de simplement en déplacer une copie sans affecter l'original.
la source
Une autre situation où vous pourriez en avoir besoin est si vous avez une collection stl de pointeurs et que vous souhaitez les modifier à l'aide de l'algorithme stl. Exemple de for_each en c ++ 98.
Sinon, si vous utilisez la signature changeObject (Object * item), vous avez une copie du pointeur, pas l'original.
la source