🔐 1. Exklusiver Besitz: std::unique_ptr & std::make_unique
Ein std::unique_ptr besitzt eine Heap-Ressource exklusiv. Er kann nicht kopiert, sondern nur via Move-Semantik (std::move) übertragen werden. Sobald der Smart Pointer den Gültigkeitsbereich (Scope) verlässt, wird der Speicher automatisch freigegeben:
#include <memory>
#include <iostream>
struct Connection {
int id;
Connection(int i) : id(i) { std::cout << "Connect " << id << "\n"; }
~Connection() { std::cout << "Disconnect " << id << "\n"; }
};
void process_connection(std::unique_ptr<Connection> conn) {
std::cout << "Processing connection " << conn->id << "\n";
} // conn wird HIER automatisch zerstört!
int main() {
// Bevorzugt: std::make_unique (Ausnahmesicher & effizient)
auto c1 = std::make_unique<Connection>(101);
// FEHLER (Kopieren verboten): auto c2 = c1;
// KORREKT (Besitz übertragen):
process_connection(std::move(c1));
// c1 ist jetzt nullptr
}
Ein std::unique_ptr ist wie ein physischer Original-Schlüssel zu einem Hotelsafe. Es gibt keine Duplikate. Wenn du den Schlüssel an einen Kollegen übergibst (std::move), sind deine eigenen Hände leer (nullptr). Beim Auschecken schließt der Schlüssel den Safe automatisch ab und löscht den Inhalt!
🤝 2. Geteilter Besitz: std::shared_ptr & std::weak_ptr
Ein std::shared_ptr verwaltet Ressourcen über einen Referenzzähler (Reference Count). Erst wenn die letzte Kopie zerstört wird (Count == 0), wird das Objekt freigegeben. Um zyklische Referenzen (Memory Leaks) zu verhindern, verwenden wir std::weak_ptr:
#include <memory>
struct Node {
int value;
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev; // Verhindert zirkuläre Referenz!
Node(int v) : value(v) {}
};
int main() {
auto n1 = std::make_shared<Node>(1);
auto n2 = std::make_shared<Node>(2);
n1->next = n2;
n2->prev = n1; // Schwache Referenz erhöht den Shared Count nicht
if (auto parent = n2->prev.lock()) {
std::cout << "Parent existiert noch: " << parent->value << "\n";
}
}
⚙️ 3. RAII Custom Deleters für C-Ressourcen
Smart Pointer können auch nicht-speicherbasierte OS-Handles (wie Dateideskriptoren, Sockets oder Mutexes) automatisch schließen:
#include <cstdio>
#include <memory>
// Custom Deleter für C-Dateizeiger
auto file_deleter = [](FILE* fp) {
if (fp) {
std::fclose(fp);
std::cout << "File handle safely closed.\n";
}
};
using SafeFile = std::unique_ptr<FILE, decltype(file_deleter)>;
SafeFile open_safe_file(const char* filename, const char* mode) {
FILE* fp = std::fopen(filename, mode);
return SafeFile(fp, file_deleter);
}
📑 4. Cheat-Sheet: Smart Pointer Vergleich
| Smart Pointer Typ | Besitzmodell | Kopierbar / Bewegbar | Typischer Anwendungsfall |
|---|---|---|---|
std::unique_ptr<T> |
Exklusiv (1 Besitzer) | Nur Move (std::move) |
Standard-Allokation, Factory Methods, RAII Guards |
std::shared_ptr<T> |
Geteilt (Ref Count) | Copy & Move erlaubt | Gemeinsame Ressourcen, Graph-Kanten, Caches |
std::weak_ptr<T> |
Nicht-besitzend (Observer) | Copy & Move | Backpointer (Parent), Caching, Cycle-Breaking |
T* (Raw Pointer) |
Kein Besitz (Observer) | Copy & Move | Nur noch für optionale Parameter / Beobachtung |
🧠 Diagnostischer Selbst-Check
Überprüfe dein Verständnis zu Speichersicherheit, Syntax und Systems-Design mit gezieltem Feedback:
🎯 Aufgaben & Checkliste in aufgabe.cpp
new / delete mehr!Im modernen C++ (seit C++14/C++20) gibt es fast keinen legitimen Grund mehr, new oder delete direkt im Anwendungscode aufzurufen. Verwende stattdessen konsequent std::make_unique und std::make_shared!