Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In der modernen C++-Programmierung enthält die Standardbibliothek intelligente Zeiger, die verwendet werden, um sicherzustellen, dass Programme frei von Arbeitsspeicher und Ressourcenlecks sind und ausnahmesicher sind.
Anwendungsmöglichkeiten für intelligente Zeiger
Intelligente Zeiger werden im std Namespace in der <Speicherheaderdatei> definiert. Sie sind für das Programmieridiom Resource Acquisition Is Initialization(RAII) von entscheidender Bedeutung. Das Hauptziel dieses Idioms besteht darin, sicherzustellen, dass der Ressourcenerwerb gleichzeitig erfolgt, wenn das Objekt initialisiert wird. Alle Ressourcen für das Objekt werden erstellt und in einer Codezeile bereit gestellt.
In der Praxis besteht das Hauptprinzip von RAII darin, die Eigentümerschaft jeder auf dem Heap allokierten Ressource auf ein stackallokiertes Objekt zu übertragen, dessen Destruktor den Code zum Löschen bzw. Freigeben der Ressource sowie für alle zugehörigen Aufräumarbeiten enthält. Zu diesen Objekten gehören dynamisch zugeordnete Speicher- oder Systemobjekthandles.
In den meisten Fällen sollten Sie, wenn Sie einen Rohzeiger oder ein Ressourcen-Handle so initialisieren, dass er auf eine tatsächliche Ressource zeigt, den Zeiger sofort an einen Smart Pointer übergeben. In modernen C++ werden unformatierte Zeiger nur in kleinen Codeblöcken mit eingeschränktem Umfang, Schleifen oder Hilfsfunktionen verwendet, bei denen die Leistung kritisch ist und keine Gefahr besteht, dass der Besitz verwechselt wird.
Im folgenden Beispiel wird die Deklaration eines Rohzeigers mit der eines intelligenten Zeigers verglichen.
void UseRawPointer()
{
// Using a raw pointer -- not recommended.
Song* pSong = new Song(L"Nothing on You", L"Bruno Mars");
// Use pSong...
// Don't forget to delete!
delete pSong;
}
void UseSmartPointer()
{
// Declare a smart pointer on stack and pass it the raw pointer.
unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars"));
// Use song2...
wstring s = song2->duration_;
//...
} // song2 is deleted automatically here.
Wie im Beispiel gezeigt, ist ein Smart Pointer eine Klassenvorlage, die Sie auf dem Stack deklarieren und mit einem rohen Zeiger initialisieren, der auf ein im Heap alloziiertes Objekt zeigt. Nachdem der intelligente Zeiger initialisiert wurde, besitzt er den Rohzeiger. Dieser Ansatz bedeutet, dass der intelligente Zeiger für das Löschen des Arbeitsspeichers verantwortlich ist, den der unformatierte Zeiger angibt.
Der intelligente Zeigerdestruktor enthält den Aufruf von delete. Da der Smart Pointer auf dem Stack deklariert ist, wird sein Destruktor aufgerufen, wenn er den Gültigkeitsbereich verlässt. Er wird auch dann aufgerufen, wenn weiter oben im Aufrufstapel eine Ausnahme ausgelöst wird.
Greifen Sie mithilfe der vertrauten Zeigeroperatoren auf den gekapselten Zeiger zu: -> und *. Die Smart-Pointer-Klasse überlädt diese Operatoren, um den gekapselten Raw Pointer zurückzugeben.
Das intelligente C++-Zeiger-Idiom ähnelt der Objekterstellung in Sprachen wie C#. Sie erstellen das Objekt, und lassen Sie es dann vom System zur richtigen Zeit löschen. Der Unterschied besteht darin, dass kein separater Garbage Collector im Hintergrund läuft. Der Speicher wird über die standardmäßigen C++-Bereichsdefinitionsregeln verwaltet, sodass die Laufzeitumgebung schneller und effizienter ist.
Wichtig
Erstellen Sie immer intelligente Zeiger in einer separaten Codezeile, niemals in einer Parameterliste. Dieser Ansatz verhindert ein subtiles Ressourcenleck aufgrund bestimmter Regeln für die Zuordnung von Parameterlisten.
Das folgende Beispiel zeigt, wie ein unique_ptr intelligenter Zeigertyp aus der C++-Standardbibliothek verwendet werden kann, um einen Zeiger auf ein großes Objekt zu kapseln.
class LargeObject
{
public:
void DoSomething(){}
};
void ProcessLargeObject(const LargeObject& lo){}
void SmartPointerDemo()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Pass a reference to a method.
ProcessLargeObject(*pLarge);
} //pLarge is deleted automatically when function block goes out of scope.
Dieses Beispiel verdeutlicht die folgenden wesentlichen Schritte für die Verwendung von intelligenten Zeigern.
Deklarieren Sie den intelligenten Zeiger als automatische (lokale) Variable. Verwenden Sie den
newOdermallocAusdruck nicht auf dem intelligenten Zeiger selbst.Als Typparameter geben Sie den Typ an, auf den der gekapselte Zeiger zeigt.
Übergeben Sie einen unformatierten Zeiger an ein
new-ed-Objekt im smarten Zeigerkonstruktor. Einige Hilfsfunktionen oder intelligente Zeigerkonstruktoren führen diese Aktion für Sie aus.Verwenden Sie die überladenen Operatoren
->und*für den Zugriff auf das Objekt.Lassen Sie den intelligenten Zeiger das Objekt löschen.
Intelligente Zeiger sind dafür konzipiert, im Hinblick auf Leistung und Arbeitsspeicher so effizient wie möglich sein. Beispielsweise ist der einzige Datenmember in unique_ptr der gekapselte Zeiger. Dies bedeutet, dass unique_ptr genau die gleiche Größe wie dieser Zeiger ist, entweder vier Bytes oder acht Bytes. Der Zugriff auf den gekapselten Zeiger mithilfe der vom Smart Pointer überladenen Operatoren * und -> ist nicht wesentlich langsamer als der direkte Zugriff auf rohe Zeiger.
Smart Pointer verfügen über eigene Mitgliedsfunktionen, auf die mit der Punktnotation zugegriffen wird. Beispielsweise verfügen einige intelligente Zeiger der C++-Standardbibliothek über eine reset Memberfunktion, die den Besitz des Zeigers freigibt. Dieser Aspekt ist nützlich, wenn Sie den vom Smart Pointer verwalteten Arbeitsspeicher freigeben möchten, bevor der Smart Pointer außer Gültigkeit gerät, wie im folgenden Beispiel gezeigt.
void SmartPointerDemo2()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Free the memory before we exit function block.
pLarge.reset();
// Do some other work...
}
Intelligente Zeiger stellen normalerweise eine Möglichkeit für den direkten Zugriff auf ihre Rohzeiger bereit. Intelligente Zeiger der C++-Standardbibliothek verfügen zu diesem Zweck über eine get Memberfunktion.
CComPtr verfügt über ein öffentliches p Klassenmitglied. Indem Sie direkten Zugriff auf den zugrunde liegenden Zeiger bereitstellen, können Sie den intelligenten Zeiger verwenden, um Speicher in Ihrem eigenen Code zu verwalten und den unformatierten Zeiger weiterhin an Code zu übergeben, der keine intelligenten Zeiger unterstützt.
void SmartPointerDemo4()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Pass raw pointer to a legacy API
LegacyLargeObjectFunction(pLarge.get());
}
Arten intelligenter Zeiger
Im folgenden Abschnitt werden die in der Windows-Programmierumgebung verfügbaren verschiedenen Arten von intelligenten Zeigern aufgeführt, und es wird beschrieben, wann sie zu verwenden sind.
Intelligente Zeiger der C++-Standardbibliothek
Verwenden Sie diese intelligenten Zeiger bevorzugt als erste Wahl, um Zeiger auf einfache C++-Objekte (POCO) zu kapseln.
unique_ptrErmöglicht genau einen Eigentümer des zugrunde liegenden Zeigers. Verwenden Sie dies als Standardoption für POCO, es sei denn, Sie wissen mit Sicherheit, dass Sie ein
shared_ptrbenötigen. Kann zu einem neuen Besitzer verschoben werden, kann aber nicht kopiert oder freigegeben werden. Ersetztauto_ptr, der veraltet ist. Ist vergleichbar mitboost::scoped_ptr.unique_ptrist klein und effizient. Die Größe entspricht der eines Zeigers, und sie unterstützt Rvalue-Referenzen für schnelles Einfügen in und Entnehmen aus Containern der C++-Standardbibliothek. Header-Datei:<memory>. Weitere Informationen finden Sie unter How to: Create and use unique_ptr instances und unique_ptr Class.shared_ptrIntelligenter Zeiger mit Referenzzählung. Verwenden Sie diese Vorgehensweise, wenn Sie mehreren Besitzern einen unformatierten Zeiger zuweisen möchten. Sie können beispielsweise eine Kopie eines Zeigers aus einem Container zurückgeben, aber auch das Original beibehalten. Der rohe Zeiger wird erst gelöscht, wenn alle Besitzer von
shared_ptrden Gültigkeitsbereich verlassen oder anderweitig die Eigentümerschaft aufgeben.Die Größe beträgt zwei Zeiger: einer für das Objekt und einer für den gemeinsamen Kontrollblock, der den Referenzzähler enthält. Headerdatei:
<memory>. Weitere Informationen finden Sie unter How to: Create and use shared_ptr instances und shared_ptr Class.weak_ptrSonderfall eines Smart Pointers zur Verwendung mit
shared_ptr. Aweak_ptrbietet Zugriff auf ein Objekt, das eine oder mehrereshared_ptrInstanzen besitzt, aber nicht an der Verweiszählung teilnimmt. Verwenden Sie diese Option, wenn Sie ein Objekt beobachten möchten, es aber nicht benötigen, um lebendig zu bleiben. Ist in einigen Fällen erforderlich, um Zirkelverweise zwischenshared_ptr-Instanzen zu unterbrechen.Headerdatei:
<memory>. Weitere Informationen finden Sie unter How to: Create and use weak_ptr instances und weak_ptr Class.
Intelligente Zeiger für COM-Objekte (klassische Windows-Programmierung)
Wenn Sie mit COM-Objekten arbeiten, kapseln Sie die Schnittstellenzeiger in einen geeigneten Smartpointer-Typ. Die ATL (Active Template Library) definiert mehrere intelligente Zeiger für verschiedene Zwecke. Sie können auch den Typ _com_ptr_t eines intelligenten Zeigers verwenden, den der Compiler einsetzt, wenn er Wrapperklassen von .tlb-Dateien erstellt. Dies ist die beste Wahl, wenn Sie die ATL-Headerdateien nicht einschließen möchten.
-
Verwenden Sie diese Klasse, es sei denn, Sie können ATL nicht verwenden. Führt Referenzzählung mithilfe der Methoden
AddRefundReleaseaus. Weitere Informationen finden Sie unter How to: Create and use CComPtr and CComQIPtr instances. -
Ähnelt
CComPtr, bietet jedoch auch eine vereinfachte Syntax für Aufrufe vonQueryInterfacebei COM-Objekten. Weitere Informationen finden Sie unter How to: Create and use CComPtr and CComQIPtr instances. -
Intelligenter Zeiger auf Objekte, die
CoTaskMemFreeverwenden, um Arbeitsspeicher freizugeben. -
Intelligenter Zeiger für Schnittstellen, die aus der globalen Schnittstellentabelle (Global Interface Table, Git) abgerufen werden.
-
Ähnelt
CComQIPtrin der Funktionalität, ist aber nicht von ATL-Headern abhängig.
ATL-Smart-Pointer für POCO-Objekte
Neben intelligenten Zeigern für COM-Objekte definiert ATL auch intelligente Zeiger und Sammlungen intelligenter Zeiger für einfache alte C++-Objekte (POCO). Bei der klassischen Windows Programmierung sind diese Typen nützliche Alternativen zu den C++-Standardbibliothekssammlungen, insbesondere, wenn die Codeübertragbarkeit nicht erforderlich ist oder wenn Sie die Programmiermodelle der C++-Standardbibliothek und ATL nicht kombinieren möchten.
-
Smart Pointer, der eindeutige Eigentümerschaft erzwingt, indem beim Kopieren die Eigentümerschaft übertragen wird. Vergleichbar mit der veralteten
std::auto_ptr-Klasse. -
Intelligenter Zeiger für Objekte, die mit der C-Funktion malloc allokiert werden.
-
Intelligenter Zeiger für Arrays, die mit
new[]zugeordnet werden. -
Klasse, die ein Array mit
CAutoPtr-Elementen kapselt. -
Klasse, die Methoden zum Bearbeiten einer Liste von
CAutoPtr-Knoten kapselt.