C++-Programmierung/ Speicherverwaltung/ Smart Pointer
Als smart guy wird auf Englisch jemand bezeichnet, der vornehm und gebildet ist. In Analogie zu diesem Begriff versteht man unter einem smart pointer ein Objekt, das sich wie ein Zeiger verhält, d.h. es muss Zeigeroperationen wie die Dereferenzierung mit *
oder den indirekten Zugriff mit ->
unterstützen. Zusätzlich zu diesen Eigenschaften geht der Smart pointer besser mit den Ressourcen um. Konkret bedeutet das, dass er darauf aufpasst, kein Speicherleck entstehen zu lassen.
Das einfachste Beispiel eines Smart pointers ist der in der C++-Bibliothek inkludierte auto_ptr
, der in der Header-Datei von <memory>
definiert wird. Hier ein Einblick in die Implementation des Auto pointers:
template <typename T>
class auto_ptr{
T* ptr;
public:
explicit auto_ptr(T* p = 0) : ptr(p) {}
~auto_ptr() { delete ptr; }
T& operator*() { return *ptr; }
T* operator->() { return ptr; }
// ...
};
Man sieht, dass auto_ptr
praktisch eine Hülle um einen primitiven Zeiger darstellt. Wichtig ist, dass der Destruktor den Speicher des Zeigers freigibt, den die Klasse als privaten Member beinhaltet.
Weil der Destruktor eines Objekts automatisch aufgerufen wird beim Verlassen des Gültigkeitsbereichs (z. B. der Methode), kann ein delete
nicht mehr vergessen werden.
Nachteile: Die Methode kann das Objekt, auf das ptr verweist, nicht als Ergebnis zurückgeben - es wird ja automatisch freigegeben. Außerdem kann der belegte Speicher nicht vorzeitig freigegeben werden - außer, man ruft explizit den Destructor auf. In Folge kann danach nicht mehr auf den auto_ptr zugegriffen werden - das Problem des dangling pointers ist in diesem Fall verlagert zu einem ungültigen Zugriff(sversuch) auf ein bereits 'destructed' Objekt.
Beispiele
BearbeitenVerwendet man Smart pointers, wird an Stelle des Codes
die kürzere Form
verwendet, und man kann darauf vertrauen, dass der Speicher, auf den der Zeiger p
verweist, am Ende der Funktion wieder freigegeben wird.
Smart Pointer mit Referenzzählung
BearbeitenEinen Nachteil hat auto_ptr
jedoch. Es darf niemals mehr als ein auto_ptr
auf den von ihm verwalteten Speicher zeigen. Diese Limitation zeigt sich z.B. beim Kopieren und Zuweisen:
In Zeile 3 wird der Kopierkonstruktor aufgerufen - dieser ist für auto_ptr
jedoch so definiert, dass er nach dem Kopieren den internen Zeiger der Vorlage (hier p.ptr
) auf null
setzt. p2
zeigt nun auf das Objekt, p
hingegen ist nun null
. Zeile 4 hat die gleichen Auswirkungen (nur umgekehrt - jetzt enthält wieder p
das Objekt und p2.ptr
ist null).
Eine mögliche Lösung zu diesem Problem ist, Smart Pointer zu benutzen, die anders mit dem Speicher umgehen und somit mehrere Zeiger auf den gleichen Speicher erlauben. Die Standardbibliothek bietet für diesen Zweck den Smart Pointer shared_ptr
im Namensraum std::tr1
an. Die Speicherverwaltung bei shared_ptr
erfolgt über Referenzzählung. Ein Zähler hält fest, wie viele Objekte auf den Speicher zeigen. Fällt der Zähler auf 0, wird der verwaltete Speicher freigegeben. Die Benutzung erfolgt ganz wie bei auto_ptr
:
Die Anweisung in Zeile 3 liefert nun eine Kopie in p2, ohne p auf null zu setzen.