Unter welchen Umständen möchten Sie Code dieser Art in c ++ verwenden?
void foo(type *&in) {...}
void fii() {
type *choochoo;
...
foo(choochoo);
}
Unter welchen Umständen möchten Sie Code dieser Art in c ++ verwenden?
void foo(type *&in) {...}
void fii() {
type *choochoo;
...
foo(choochoo);
}
Antworten:
Sie möchten einen Zeiger als Referenz übergeben, wenn Sie den Zeiger anstelle des Objekts ändern müssen, auf das der Zeiger zeigt.
Dies ähnelt dem Grund, warum Doppelzeiger verwendet werden. Die Verwendung eines Verweises auf einen Zeiger ist etwas sicherer als die Verwendung von Zeigern.
delete
freigeben kann, indem er diesen Zeiger an einer anderen Stelle im Speicher verwendet oder erneut bindet? Oder habe ich es falsch verstanden?
[]
Bediener. Eine mögliche (und tatsächlich natürliche) Signatur hierfür wäre const T &operator[](size_t index) const
. Aber du könntest es auch haben T &operator[](size_t index)
. Sie könnten beide gleichzeitig haben. Letzteres würde Sie Dinge wie tun lassen myArray[jj] = 42
. Gleichzeitig geben Sie keinen Zeiger auf Ihre Daten an, sodass der Anrufer nicht mit dem Speicher herumspielen kann (z. B. versehentlich löschen).
50% der C ++ - Programmierer setzen ihre Zeiger nach dem Löschen gerne auf null:
template<typename T>
void moronic_delete(T*& p)
{
delete p;
p = nullptr;
}
Ohne die Referenz würden Sie nur eine lokale Kopie des Zeigers ändern, ohne den Anrufer zu beeinflussen.
paranoid_delete
, aber Puppy hat sie umbenannt moronic_delete
. Er gehört wahrscheinlich zu den anderen 50%;) Wie auch immer, die kurze Antwort ist, dass Einstellungszeiger auf null nach delete
fast nie nützlich sind (weil sie sowieso außerhalb des Gültigkeitsbereichs liegen sollten) und oft die Erkennung von "Verwendung nach freien" Fehlern behindern.
delete
im Allgemeinen dumm zu sein. Welpe, ich habe ein bisschen gegoogelt, ich kann nicht verstehen, warum Löschen völlig nutzlos ist (vielleicht bin ich auch ein schrecklicher Googler;)). Können Sie etwas mehr erklären oder einen Link bereitstellen?
Davids Antwort ist richtig, aber wenn sie noch ein wenig abstrakt ist, hier zwei Beispiele:
Möglicherweise möchten Sie alle freigegebenen Zeiger auf Null setzen, um Speicherprobleme früher zu erkennen. C-Stil würden Sie tun:
void freeAndZero(void** ptr)
{
free(*ptr);
*ptr = 0;
}
void* ptr = malloc(...);
...
freeAndZero(&ptr);
In C ++ können Sie Folgendes tun, um dasselbe zu tun:
template<class T> void freeAndZero(T* &ptr)
{
delete ptr;
ptr = 0;
}
int* ptr = new int;
...
freeAndZero(ptr);
Beim Umgang mit verknüpften Listen - oft einfach als Zeiger auf einen nächsten Knoten dargestellt:
struct Node
{
value_t value;
Node* next;
};
In diesem Fall müssen Sie beim Einfügen in die leere Liste unbedingt den eingehenden Zeiger ändern, da das Ergebnis nicht NULL
mehr der Zeiger ist. Dies ist ein Fall, in dem Sie einen externen Zeiger von einer Funktion aus ändern, sodass er in seiner Signatur einen Verweis auf den Zeiger enthält:
void insert(Node* &list)
{
...
if(!list) list = new Node(...);
...
}
In dieser Frage gibt es ein Beispiel .
Ich musste Code wie diesen verwenden, um Funktionen bereitzustellen, um einem übergebenen Zeiger Speicher zuzuweisen und seine Größe zurückzugeben, weil mein Unternehmen mir mithilfe der STL "Objekt" gibt
int iSizeOfArray(int* &piArray) {
piArray = new int[iNumberOfElements];
...
return iNumberOfElements;
}
Es ist nicht schön, aber der Zeiger muss als Referenz übergeben werden (oder Doppelzeiger verwenden). Wenn nicht, wird Speicher einer lokalen Kopie des Zeigers zugewiesen, wenn er als Wert übergeben wird, was zu einem Speicherverlust führt.
Ein Beispiel ist, wenn Sie eine Parser-Funktion schreiben und ihr einen Quellzeiger zum Lesen übergeben, wenn die Funktion diesen Zeiger hinter das letzte Zeichen verschieben soll, das vom Parser korrekt erkannt wurde. Die Verwendung eines Verweises auf einen Zeiger macht dann deutlich, dass die Funktion den ursprünglichen Zeiger bewegt, um seine Position zu aktualisieren.
Im Allgemeinen verwenden Sie Verweise auf Zeiger, wenn Sie einen Zeiger auf eine Funktion übergeben und diesen ursprünglichen Zeiger an eine andere Position verschieben möchten, anstatt nur eine Kopie davon zu verschieben, ohne das Original zu beeinflussen.
Eine andere Situation, in der Sie dies möglicherweise benötigen, besteht darin, dass Sie über eine stl-Sammlung von Zeigern verfügen und diese mithilfe des stl-Algorithmus ändern möchten. Beispiel für 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();
}
};
Andernfalls haben Sie bei Verwendung der Signatur changeObject (Object * item) eine Kopie des Zeigers und keine Originalkopie.