Dynamische Zuweisung ist nur erforderlich, wenn die Lebensdauer des Objekts anders sein sollte als der Bereich, in dem es erstellt wird (Dies gilt sowohl für die Verkleinerung als auch für die Vergrößerung des Bereichs) und Sie einen bestimmten Grund haben, aus dem das Speichern nach Wert nicht funktioniert.
Zum Beispiel:
std::vector<int> *createVector(); // Bad
std::vector<int> createVector(); // Good
auto v = new std::vector<int>(); // Bad
auto result = calculate(/*optional output = */ v);
auto v = std::vector<int>(); // Good
auto result = calculate(/*optional output = */ &v);
Von C++11 an haben wir std::unique_ptr
für den Umgang mit zugewiesenem Speicher, der die Eigentumsrechte an dem zugewiesenen Speicher enthält. std::shared_ptr
wurde für den Fall geschaffen, dass Sie das Eigentum teilen müssen. (Sie werden dies weniger brauchen, als Sie in einem guten Programm erwarten würden)
Die Erstellung einer Instanz ist wirklich einfach:
auto instance = std::make_unique<Class>(/*args*/); // C++14
auto instance = std::unique_ptr<Class>(new Class(/*args*/)); // C++11
auto instance = std::make_unique<Class[]>(42); // C++14
auto instance = std::unique_ptr<Class[]>(new Class[](42)); // C++11
C++17 fügt außerdem hinzu std::optional
was verhindern kann, dass Sie Speicherzuweisungen benötigen
auto optInstance = std::optional<Class>{};
if (condition)
optInstance = Class{};
Sobald 'instance' den Anwendungsbereich verlässt, wird der Speicher aufgeräumt. Auch die Übertragung des Eigentums ist einfach:
auto vector = std::vector<std::unique_ptr<Interface>>{};
auto instance = std::make_unique<Class>();
vector.push_back(std::move(instance)); // std::move -> transfer (most of the time)
Wann brauchen Sie also noch new
? Ab C++11 fast nie. Die meisten von Ihnen verwenden std::make_unique
bis zu einem Punkt, an dem man auf eine API stößt, die das Eigentum über rohe Zeiger überträgt.
auto instance = std::make_unique<Class>();
legacyFunction(instance.release()); // Ownership being transferred
auto instance = std::unique_ptr<Class>{legacyFunction()}; // Ownership being captured in unique_ptr
In C++98/03 müssen Sie die Speicherverwaltung manuell vornehmen. Wenn dies bei Ihnen der Fall ist, versuchen Sie, auf eine neuere Version des Standards zu aktualisieren. Wenn Sie nicht weiterkommen:
auto instance = new Class(); // Allocate memory
delete instance; // Deallocate
auto instances = new Class[42](); // Allocate memory
delete[] instances; // Deallocate
Stellen Sie sicher, dass Sie die Eigentümerschaft korrekt verfolgen, um Speicherlecks zu vermeiden! Move-Semantik funktioniert auch noch nicht.
Wann brauchen wir also malloc in C++? Der einzige triftige Grund wäre die Zuweisung von Speicher und dessen spätere Initialisierung durch die Platzierung von new.
auto instanceBlob = std::malloc(sizeof(Class)); // Allocate memory
auto instance = new(instanceBlob)Class{}; // Initialize via constructor
instance.~Class(); // Destroy via destructor
std::free(instanceBlob); // Deallocate the memory
Auch wenn das oben Gesagte zutrifft, kann dies auch über einen new-operator erfolgen. std::vector
ist dafür ein gutes Beispiel.
Schließlich haben wir noch den Elefanten im Raum: C
. Wenn Sie mit einer C-Bibliothek arbeiten müssen, bei der Speicher im C++-Code alloziert und im C-Code wieder freigegeben wird (oder umgekehrt), sind Sie gezwungen, malloc/free zu verwenden.
Wenn dies der Fall ist, vergessen Sie virtuelle Funktionen, Mitgliedsfunktionen, Klassen ... Nur Strukturen mit PODs darin sind erlaubt.
Einige Ausnahmen von den Regeln:
- Sie schreiben eine Standardbibliothek mit fortgeschrittenen Datenstrukturen, für die malloc geeignet ist
- Sie müssen große Mengen an Speicher zuweisen (Kopie einer 10-GB-Datei im Speicher?)
- Sie haben Werkzeuge, die Sie daran hindern, bestimmte Konstrukte zu verwenden
- Sie müssen einen unvollständigen Typ speichern