Dans quelles circonstances voudriez-vous utiliser du code de cette nature en C ++?
void foo(type *&in) {...}
void fii() {
type *choochoo;
...
foo(choochoo);
}
Dans quelles circonstances voudriez-vous utiliser du code de cette nature en C ++?
void foo(type *&in) {...}
void fii() {
type *choochoo;
...
foo(choochoo);
}
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.
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 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).
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.
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 delete
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:
void freeAndZero(void** ptr)
{
free(*ptr);
*ptr = 0;
}
void* ptr = malloc(...);
...
freeAndZero(&ptr);
En C ++ pour faire de même, vous pouvez faire:
template<class T> void freeAndZero(T* &ptr)
{
delete ptr;
ptr = 0;
}
int* ptr = new int;
...
freeAndZero(ptr);
Lorsqu'il s'agit de listes chaînées - souvent simplement représentées comme des pointeurs vers un nœud suivant:
struct Node
{
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 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:
void insert(Node* &list)
{
...
if(!list) list = new Node(...);
...
}
Il y a un exemple dans cette question .
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 = new int[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.
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.
struct Storage {
typedef std::list<Object*> ObjectList;
ObjectList objects;
void change() {
typedef void (*ChangeFunctionType)(Object*&);
std::for_each<ObjectList::iterator, ChangeFunctionType>
(objects.begin(), objects.end(), &Storage::changeObject);
}
static void changeObject(Object*& item) {
delete item;
item = 0;
if (someCondition) item = new Object();
}
};
Sinon, si vous utilisez la signature changeObject (Object * item), vous avez une copie du pointeur, pas l'original.