Smart Pointers in C++
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 15 von 104.
Smart Pointer sind Wrapper-Klassen, die den Heap-Speicher automatisch für Sie verwalten. Sie machen manuelle delete-Aufrufe überflüssig, indem sie den Speicher bereinigen, wenn der Pointer seinen Gültigkeitsbereich verlässt.
Binden Sie den <memory>-Header ein, um Smart Pointer zu verwenden.
unique_ptr - exklusives Eigentum. Nur ein Pointer kann die Ressource zu einem Zeitpunkt besitzen.
#include <memory>
std::unique_ptr<Player> player = std::make_unique<Player>("Hero");
player->attack(); // Verwendung wie ein normaler Zeiger
// Kann nicht kopiert werden, aber der Besitz kann übertragen werden
std::unique_ptr<Player> player2 = std::move(player);
// player ist jetzt nullptrshared_ptr - Gemeinsamer Besitz. Mehrere Pointer können dieselbe Ressource besitzen. Der Speicher wird freigegeben, wenn der letzte Besitzer zerstört wird.
std::shared_ptr<Player> p1 = std::make_shared<Player>("Hero");
std::shared_ptr<Player> p2 = p1; // Beide besitzen die Ressource
std::cout << p1.use_count(); // Gibt 2 ausweak_ptr - nicht-besitzender Beobachter eines shared_ptr. Nützlich, um zirkuläre Referenzen aufzubrechen.
std::shared_ptr<Player> shared = std::make_shared<Player>("Hero");
std::weak_ptr<Player> weak = shared;
if (auto locked = weak.lock()) { // In shared_ptr umwandeln
locked->attack();
}Verwenden Sie standardmäßig unique_ptr für alleiniges Eigentum. Verwenden Sie shared_ptr, wenn mehrere Objekte eine Ressource gemeinsam nutzen müssen. Verwenden Sie weak_ptr, um zu beobachten, ohne die Lebensdauer zu verlängern.
Bevorzugen Sie make_unique und make_shared gegenüber einfachem new.
Aufgabe
EinfachLassen Sie uns ein Dokumentenfreigabesystem erstellen, das die verschiedenen Eigentumsmodelle von Smart Pointern demonstriert. Sie werden sehen, wie unique_ptr exklusives Eigentum erzwingt, wie shared_ptr mehrere Eigentümer ermöglicht und wie weak_ptr beobachten kann, ohne die Lebensdauer zu beeinflussen.
Sie werden zwei Dateien erstellen, um Ihren Code zu organisieren:
Document.h: Definieren Sie eine KlasseDocument, die ein freigebbares Dokument darstellt. Sie sollte Folgendes enthalten:- Ein privates Attribut
title(String) - Einen Konstruktor, der einen Titel entgegennimmt und
"Document created: <title>"ausgibt - Einen Destruktor, der
"Document deleted: <title>"ausgibt - Eine Methode
getTitle(), die den Titel zurückgibt
- Ein privates Attribut
main.cpp: Demonstrieren Sie alle drei Smart-Pointer-Typen, indem Sie einen Dokumenttitel aus der Eingabe lesen und dann:- Einen
unique_ptrauf ein Document mit dem eingegebenen Titel erstellen "Unique owner: <title>"ausgeben- Das Eigentum mit
std::moveauf einen anderenunique_ptrübertragen "Ownership transferred"ausgeben- Einen
shared_ptrauf ein neues Document mit dem Titel"SharedDoc"erstellen - Einen zweiten
shared_ptrerstellen, der auf dasselbe Dokument zeigt "Shared owners: <count>"unter Verwendung vonuse_count()ausgeben- Einen
weak_ptrvon einem der Shared Pointer erstellen lock()verwenden, um über den Weak Pointer auf das Dokument zuzugreifen und"Weak access: <title>"auszugeben
- Einen
Denken Sie daran, <memory> für Smart Pointer einzubinden. Verwenden Sie std::make_unique und std::make_shared, um Ihre Smart Pointer zu erstellen. Die Destruktor-Meldungen helfen Ihnen zu sehen, wann jedes Dokument am Ende des Programms automatisch bereinigt wird.
Binden Sie Ihre Header-Datei in main.cpp mit #include "Document.h" ein.
Spickzettel
Smart Pointer sind Wrapper-Klassen, die den Heap-Speicher automatisch verwalten. Binden Sie den Header <memory> ein, um sie zu verwenden.
unique_ptr
Exklusiver Besitz – nur ein Pointer kann die Ressource gleichzeitig besitzen:
std::unique_ptr<Player> player = std::make_unique<Player>("Hero");
player->attack(); // Wie einen regulären Pointer verwenden
// Transfer ownership with std::move
std::unique_ptr<Player> player2 = std::move(player);
// player is now nullptrshared_ptr
Geteilter Besitz – mehrere Pointer können dieselbe Ressource besitzen. Der Speicher wird freigegeben, wenn der letzte Besitzer zerstört wird:
std::shared_ptr<Player> p1 = std::make_shared<Player>("Hero");
std::shared_ptr<Player> p2 = p1; // Beide besitzen die Ressource
std::cout << p1.use_count(); // Gibt 2 ausweak_ptr
Nicht-besitzender Beobachter eines shared_ptr. Nützlich, um zirkuläre Referenzen aufzubrechen:
std::shared_ptr<Player> shared = std::make_shared<Player>("Hero");
std::weak_ptr<Player> weak = shared;
if (auto locked = weak.lock()) { // In shared_ptr umwandeln
locked->attack();
}Best Practices: Verwenden Sie standardmäßig unique_ptr für exklusiven Besitz. Verwenden Sie shared_ptr, wenn mehrere Objekte eine Ressource teilen müssen. Verwenden Sie weak_ptr, um zu beobachten, ohne die Lebensdauer zu verlängern. Bevorzugen Sie make_unique und make_shared gegenüber einem rohen new.
Probier es selbst
#include <iostream>
#include <memory>
#include <string>
#include "Document.h"
using namespace std;
int main() {
string inputTitle;
cin >> inputTitle;
// TODO: Erstelle einen unique_ptr für ein Document mit inputTitle
// unter Verwendung von std::make_unique
// TODO: Gib "Unique owner: <title>" aus
// TODO: Übertrage den Besitz auf einen anderen unique_ptr mittels std::move
// TODO: Gib "Ownership transferred" aus
// TODO: Erstelle einen shared_ptr für ein neues Document mit dem Titel "SharedDoc"
// unter Verwendung von std::make_shared
// TODO: Erstelle einen zweiten shared_ptr, der auf dasselbe Dokument zeigt
// TODO: Gib "Shared owners: <count>" unter Verwendung von use_count() aus
// TODO: Erstelle einen weak_ptr von einem der Shared Pointer
// TODO: Verwende lock(), um über den weak_ptr auf das Dokument zuzugreifen
// und gib "Weak access: <title>" aus
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container