In quali circostanze vorresti usare codice di questa natura in c ++?
void foo(type *&in) {...}
void fii() {
type *choochoo;
...
foo(choochoo);
}
In quali circostanze vorresti usare codice di questa natura in c ++?
void foo(type *&in) {...}
void fii() {
type *choochoo;
...
foo(choochoo);
}
Risposte:
Si desidera passare un puntatore per riferimento se è necessario modificare il puntatore anziché l'oggetto a cui punta.
Questo è simile al motivo per cui vengono utilizzati i doppi puntatori; l'utilizzo di un riferimento a un puntatore è leggermente più sicuro rispetto all'utilizzo dei puntatori.
delete
o riassociare quel puntatore a qualche altro posto nella memoria? O ho sbagliato?
[]
operatore. Una possibile (e in effetti naturale) firma per questo sarebbe const T &operator[](size_t index) const
. Ma potresti anche avere T &operator[](size_t index)
. Potresti averli entrambi allo stesso tempo. Quest'ultimo ti permetterebbe di fare cose come myArray[jj] = 42
. Allo stesso tempo, non stai fornendo un puntatore ai tuoi dati, quindi il chiamante non può interferire con la memoria (ad esempio, cancellarla accidentalmente).
Il 50% dei programmatori C ++ preferisce impostare i propri puntatori su null dopo un'eliminazione:
template<typename T>
void moronic_delete(T*& p)
{
delete p;
p = nullptr;
}
Senza il riferimento, cambieresti solo una copia locale del puntatore, senza influire sul chiamante.
paranoid_delete
, ma Puppy lo ha rinominato moronic_delete
. Probabilmente appartiene al restante 50%;) Ad ogni modo, la risposta breve è che i puntatori di impostazioni a null after non delete
sono quasi mai utili (perché dovrebbero comunque uscire dall'ambito) e spesso ostacola il rilevamento di bug "use after free".
delete
sia stupido in generale. Puppy, ho fatto un po 'di googling, non riesco a capire perché eliminare sia completamente inutile (forse sono anche un pessimo googler;)). Puoi spiegare un po 'di più o fornire un link?
La risposta di David è corretta, ma se è ancora un po 'astratta, ecco due esempi:
Potresti voler azzerare tutti i puntatori liberati per rilevare prima i problemi di memoria. Stile C faresti:
void freeAndZero(void** ptr)
{
free(*ptr);
*ptr = 0;
}
void* ptr = malloc(...);
...
freeAndZero(&ptr);
In C ++ per fare lo stesso, potresti fare:
template<class T> void freeAndZero(T* &ptr)
{
delete ptr;
ptr = 0;
}
int* ptr = new int;
...
freeAndZero(ptr);
Quando si ha a che fare con elenchi collegati, spesso rappresentati semplicemente come puntatori a un nodo successivo:
struct Node
{
value_t value;
Node* next;
};
In questo caso, quando si inserisce nella lista vuota è necessario necessariamente modificare il puntatore in entrata perché il risultato non è più il NULL
puntatore. Questo è il caso in cui modifichi un puntatore esterno da una funzione, quindi avrebbe un riferimento al puntatore nella sua firma:
void insert(Node* &list)
{
...
if(!list) list = new Node(...);
...
}
C'è un esempio in questa domanda .
Ho dovuto usare un codice come questo per fornire funzioni per allocare memoria a un puntatore passato e restituirne le dimensioni perché la mia azienda "oggetto" per me usando l'STL
int iSizeOfArray(int* &piArray) {
piArray = new int[iNumberOfElements];
...
return iNumberOfElements;
}
Non è carino, ma il puntatore deve essere passato per riferimento (o utilizzare il doppio puntatore). In caso contrario, la memoria viene allocata a una copia locale del puntatore se viene passata per valore che si traduce in una perdita di memoria.
Un esempio è quando si scrive una funzione parser e le si passa un puntatore sorgente da cui leggere, se si suppone che la funzione spinga quel puntatore in avanti dietro l'ultimo carattere che è stato correttamente riconosciuto dal parser. L'uso di un riferimento a un puntatore rende chiaro quindi che la funzione sposterà il puntatore originale per aggiornare la sua posizione.
In generale, si utilizzano riferimenti ai puntatori se si desidera passare un puntatore a una funzione e lasciare che sposti il puntatore originale in un'altra posizione invece di spostarne solo una copia senza influire sull'originale.
Un'altra situazione in cui potrebbe essere necessario questo è se si dispone di una raccolta di puntatori stl e si desidera modificarli utilizzando l'algoritmo stl. Esempio di for_each in 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();
}
};
Altrimenti, se usi la firma changeObject (Object * item) hai una copia del puntatore, non quella originale.