Ich sehe in C++, dass es mehrere Möglichkeiten gibt, Daten zuzuweisen und freizugeben, und ich verstehe, dass Sie beim Aufrufen von malloc
den Operator free
aufrufen und beim Verwenden des Operators new
koppeln sollten mit delete
und es ist ein Fehler, die beiden zu mischen (z. B. Aufrufen von free()
für etwas, das mit dem Operator new
erstellt wurde), aber mir ist nicht klar, wann Ich sollte malloc
/free
verwenden und wann ich new
/delete
in meinen Programmen der realen Welt verwenden sollte.
Wenn Sie ein C++ - Experte sind, teilen Sie mir bitte alle Faustregeln oder Konventionen mit, die Sie in dieser Hinsicht befolgen.
Sofern Sie nicht gezwungen sind, C zu verwenden, sollten Sie niemals verwendenmalloc
. Verwenden Sie immer new
.
Wenn Sie einen großen Datenblock benötigen, tun Sie einfach Folgendes:
char *pBuffer = new char[1024];
Seien Sie vorsichtig, obwohl dies nicht korrekt ist:
//This is incorrect - may delete only one element, may corrupt the heap, or worse...
delete pBuffer;
Sie sollten dies stattdessen tun, wenn Sie ein Array von Daten löschen:
//This deletes all items in the array
delete[] pBuffer;
Das Schlüsselwort new
ist die C++ - Methode, mit der sichergestellt wird, dass Ihr Typ sein Konstruktor heißt hat. Das Schlüsselwort new
ist auch mehr typsicher, wohingegen malloc
überhaupt nicht typsicher ist.
Ich könnte mir nur vorstellen, dass die Verwendung von malloc
von Vorteil ist, wenn Sie die Größe Ihres Puffers ändern von Daten benötigen. Das Schlüsselwort new
hat keine analoge Bedeutung wie realloc
. Die Funktion realloc
kann möglicherweise die Größe eines Speicherbereichs für Sie effizienter erweitern.
Es ist erwähnenswert, dass Sie new
/free
und malloc
/delete
nicht mischen können.
Hinweis: Einige Antworten in dieser Frage sind ungültig.
int* p_scalar = new int(5); // Does not create 5 elements, but initializes to 5
int* p_array = new int[5]; // Creates 5 elements
Die kurze Antwort lautet: Verwenden Sie malloc
für C++ nicht ohne einen wirklich guten Grund dafür. malloc
weist bei der Verwendung mit C++ eine Reihe von Mängeln auf, die new
überwinden sollte.
malloc
ist in keiner Weise typsicher. In C++ müssen Sie die Rückgabe von void*
. Dies führt möglicherweise zu einer Reihe von Problemen:
#include <stdlib.h>
struct foo {
double d[5];
};
int main() {
foo *f1 = malloc(1); // error, no cast
foo *f2 = static_cast<foo*>(malloc(sizeof(foo)));
foo *f3 = static_cast<foo*>(malloc(1)); // No error, bad
}
Es ist schlimmer als das. Wenn der fragliche Typ POD (einfache alte Daten) ist, können Sie malloc
halbwegs sinnvoll verwenden, um Speicher dafür zuzuweisen, wie f2
tut im ersten Beispiel.
Es ist jedoch nicht so offensichtlich, ob ein Typ POD ist. Die Tatsache, dass es für einen bestimmten Typ möglich ist, von POD zu Non-POD zu wechseln, ohne dass ein Compiler-Fehler auftritt und möglicherweise sehr schwer zu debuggen ist, ist ein wesentlicher Faktor. Wenn zum Beispiel jemand (möglicherweise ein anderer Programmierer, der viel später während der Wartung eine Änderung vornehmen würde, die dazu führte, dass foo
nicht mehr POD ist, würde zur Kompilierungszeit kein offensichtlicher Fehler auftreten, wie Sie hoffen würden, z. B .:
struct foo {
double d[5];
virtual ~foo() { }
};
würde das malloc
von f2
wird auch schlecht, ohne offensichtliche Diagnose. Das Beispiel hier ist trivial, aber es ist möglich, Non-PODness versehentlich viel weiter weg einzuführen (z. B. in einer Basisklasse, indem ein Non-POD-Member hinzugefügt wird). Wenn Sie C++ 11/boost haben, können Sie is_pod
um zu überprüfen, ob diese Annahme korrekt ist und einen Fehler zu erzeugen, wenn dies nicht der Fall ist:
#include <type_traits>
#include <stdlib.h>
foo *safe_foo_malloc() {
static_assert(std::is_pod<foo>::value, "foo must be POD");
return static_cast<foo*>(malloc(sizeof(foo)));
}
Obwohl boost nicht feststellen kann, ob ein Typ POD ist, ohne C++ 11 oder einige andere Compiler-Erweiterungen.
malloc
gibt NULL
zurück, wenn die Zuordnung fehlschlägt. new
wirft std::bad_alloc
. Das Verhalten der späteren Verwendung eines Zeigers NULL
ist undefiniert. Eine Ausnahme hat eine saubere Semantik, wenn sie ausgelöst wird und von der Fehlerquelle stammt. Das Umschließen von malloc
mit einem entsprechenden Test bei jedem Aufruf erscheint mühsam und fehleranfällig. (Sie müssen nur einmal vergessen, um all diese gute Arbeit rückgängig zu machen). Eine Ausnahme kann sich auf eine Ebene ausbreiten, auf der ein Aufrufer sie sinnvoll verarbeiten kann, wobei es sehr viel schwieriger ist, sie sinnvoll zurückzugeben, da NULL
. Wir könnten unser safe_foo_malloc
Funktion um eine Ausnahme auszulösen oder das Programm zu verlassen oder einen Handler aufzurufen:
#include <type_traits>
#include <stdlib.h>
void my_malloc_failed_handler();
foo *safe_foo_malloc() {
static_assert(std::is_pod<foo>::value, "foo must be POD");
foo *mem = static_cast<foo*>(malloc(sizeof(foo)));
if (!mem) {
my_malloc_failed_handler();
// or throw ...
}
return mem;
}
Grundsätzlich ist malloc
ein C-Feature und new
ein C++ - Feature. Infolgedessen funktioniert malloc
nicht gut mit Konstruktoren, sondern es wird nur versucht, einen Teil der Bytes zuzuweisen. Wir könnten unser safe_foo_malloc
zur weiteren Verwendung von Placement new
:
#include <stdlib.h>
#include <new>
void my_malloc_failed_handler();
foo *safe_foo_malloc() {
void *mem = malloc(sizeof(foo));
if (!mem) {
my_malloc_failed_handler();
// or throw ...
}
return new (mem)foo();
}
Unser safe_foo_malloc
Funktion ist nicht sehr allgemein - im Idealfall möchten wir etwas, das mit jedem Typ umgehen kann, nicht nur mit foo
. Wir können dies mit Vorlagen und variablen Vorlagen für nicht standardmäßige Konstruktoren erreichen:
#include <functional>
#include <new>
#include <stdlib.h>
void my_malloc_failed_handler();
template <typename T>
struct alloc {
template <typename ...Args>
static T *safe_malloc(Args&&... args) {
void *mem = malloc(sizeof(T));
if (!mem) {
my_malloc_failed_handler();
// or throw ...
}
return new (mem)T(std::forward(args)...);
}
};
Jetzt haben wir bei der Behebung aller bisher identifizierten Probleme den Standardoperator new
praktisch neu erfunden. Wenn Sie malloc
und Placement new
verwenden, können Sie auch new
verwenden, um damit zu beginnen!
Aus dem C++ FQA Lite :
[16.4] Warum sollte ich new anstatt trustworthy old malloc () verwenden?
FAQ: new/delete Konstruktor/Destruktor aufrufen; neu ist typsicher, malloc nicht; new kann von einer Klasse überschrieben werden.
FQA: Die in FAQ) genannten Tugenden von new sind keine Tugenden, da Konstruktoren, Destruktoren und Operatorüberladungen Müll sind (siehe, was passiert, wenn Sie keine Garbage Collection haben?) Und die Typensicherheit Das Problem ist hier wirklich winzig (normalerweise müssen Sie das von malloc zurückgegebene void * in den richtigen Zeigertyp umwandeln, um es einer typisierten Zeigervariablen zuzuweisen, die möglicherweise ärgerlich, aber keineswegs "unsicher" ist).
Oh, und die Verwendung von vertrauenswürdigen alten Mallocs ermöglicht die Verwendung von ebenso vertrauenswürdigen und alten Reallocs. Schade, dass wir keinen glänzenden neuen Operator haben, der erneuert oder so.
Dennoch ist new nicht schlecht genug, um eine Abweichung von dem in einer Sprache verwendeten allgemeinen Stil zu rechtfertigen, selbst wenn die Sprache C++ ist. Insbesondere Klassen mit nicht-trivialen Konstruktoren verhalten sich fatal, wenn Sie die Objekte einfach mallocieren. Warum also nicht den gesamten Code neu verwenden? Die Leute überlasten die Betreiber selten mit neuen, so dass es Ihnen wahrscheinlich nicht zu viel im Weg steht. Und wenn sie neu überladen, können Sie sie jederzeit auffordern, aufzuhören.
Entschuldigung, ich konnte einfach nicht widerstehen. :)
Verwenden Sie in C++ immer new. Wenn Sie einen Block mit nicht typisiertem Speicher benötigen, können Sie operator new direkt verwenden:
void *p = operator new(size);
...
operator delete(p);
Verwenden Sie malloc
und free
nur zum Zuweisen von Speicher Das wird von c-centric Libraries und APIs verwaltet. Verwenden Sie new
und delete
(und das []
variant) für alles, was du kontrollierst.
new vs malloc ()
1) new
ist ein Operator, während malloc()
ein Funktion ist.
2) new
ruft Konstruktoren auf, malloc()
nicht.
3) new
gibt genauer Datentyp zurück, während malloc()
void * zurückgibt.
4) new
gibt niemals ein NULL (wird bei einem Fehler ausgelöst) zurück, während malloc()
NULL zurückgibt
5) Neuzuweisung von Speicher, der von new
nicht behandelt wird, während malloc()
kann
Um Ihre Frage zu beantworten, sollten Sie wissen, der Unterschied zwischen malloc
und new
. Der Unterschied ist einfach:
malloc
reserviert Speicher, während new
reserviert Speicher UND ruft den Konstruktor auf des Objekts, für das Sie Speicher reservieren.
Sofern Sie nicht auf C beschränkt sind, sollten Sie malloc niemals verwenden, insbesondere nicht, wenn Sie mit C++ - Objekten arbeiten. Das wäre ein Rezept, um Ihr Programm zu brechen.
Auch der Unterschied zwischen free
und delete
ist ziemlich gleich. Der Unterschied besteht darin, dass delete
zusätzlich zur Speicherfreigabe den Destruktor Ihres Objekts aufruft.
Es gibt einen großen Unterschied zwischen malloc
und new
. malloc
reserviert Speicher. Dies ist in Ordnung für C, da in C ein Speicherblock ein Objekt ist.
Wenn Sie sich in C++ nicht mit POD-Typen befassen (die C-Typen ähneln), müssen Sie einen Konstruktor an einem Speicherort aufrufen, um tatsächlich ein Objekt dort zu haben. Non-POD-Typen sind in C++ sehr verbreitet, da viele C++ - Features ein Objekt automatisch zu Non-POD machen.
new
reserviert Speicher und erstellt ein Objekt an diesem Speicherort. Für Nicht-POD-Typen bedeutet dies, einen Konstruktor aufzurufen.
Wenn du so etwas machst:
non_pod_type* p = (non_pod_type*) malloc(sizeof *p);
Der Zeiger, den Sie erhalten, kann nicht dereferenziert werden, da er nicht auf ein Objekt verweist. Sie müssen einen Konstruktor dafür aufrufen, bevor Sie ihn verwenden können (und dies geschieht mit placement new
).
Wenn Sie andererseits Folgendes tun:
non_pod_type* p = new non_pod_type();
Sie erhalten einen Zeiger, der immer gültig ist, da new
ein Objekt erstellt hat.
Selbst für POD-Typen gibt es einen signifikanten Unterschied zwischen den beiden:
pod_type* p = (pod_type*) malloc(sizeof *p);
std::cout << p->foo;
Dieser Code gibt einen nicht angegebenen Wert aus, da die von malloc
erstellten POD-Objekte nicht initialisiert werden.
Mit new
können Sie einen Konstruktor angeben, der aufgerufen werden soll, und so einen genau definierten Wert erhalten.
pod_type* p = new pod_type();
std::cout << p->foo; // prints 0
Wenn Sie es wirklich wollen, können Sie new
verwenden, um nicht initialisierte POD-Objekte zu erhalten. Siehe diese andere Antwort für weitere Informationen dazu.
Ein weiterer Unterschied ist das Verhalten beim Versagen. Wenn die Speicherzuweisung fehlschlägt, gibt malloc
einen Nullzeiger zurück, während new
eine Ausnahme auslöst.
Ersteres erfordert, dass Sie jeden zurückgegebenen Zeiger testen, bevor Sie ihn verwenden, während letzterer immer gültige Zeiger erzeugt.
Aus diesen Gründen sollten Sie in C++ - Code new
und nicht malloc
verwenden. Aber selbst dann sollten Sie new
nicht "im Freien" verwenden, da es Ressourcen beschafft, die Sie später freigeben müssen. Wenn Sie new
verwenden, sollten Sie das Ergebnis sofort an eine Ressourcenverwaltungsklasse übergeben:
std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak
Es gibt einige Dinge, die new
nicht malloc
tun:
new
erstellt das Objekt, indem der Konstruktor dieses Objekts aufgerufen wirdnew
erfordert keine Typumwandlung des zugewiesenen Speichers.Wenn Sie also malloc
verwenden, müssen Sie explizit vorgehen, was nicht immer praktisch ist. Darüber hinaus kann new
überladen werden, malloc
jedoch nicht.
Wenn Sie mit Daten arbeiten, die nicht erstellt/zerstört werden müssen und Neuzuordnungen erfordern (z. B. eine große Anzahl von Ints), dann ist malloc/free meiner Meinung nach eine gute Wahl, da es Ihnen eine Neuzuordnung ermöglicht, die viel schneller ist als Neuzuordnungen -delete (es ist auf meiner Linux-Box, aber ich denke, das kann plattformabhängig sein). Wenn Sie mit C++ - Objekten arbeiten, die kein POD sind und eine Konstruktion/Zerstörung erfordern, müssen Sie die Operatoren new und delete verwenden.
Wie auch immer, ich verstehe nicht, warum Sie nicht beide verwenden sollten (vorausgesetzt, Sie geben Ihren Malloced Memory frei und löschen mit new zugewiesene Objekte), wenn Sie den Geschwindigkeitsschub nutzen können (manchmal einen signifikanten, wenn Sie große Arrays neu zuweisen) von POD), die Realloc Ihnen geben kann.
Sofern Sie es nicht benötigen, sollten Sie sich in C++ an new/delete halten.
Wenn Sie C++ verwenden, versuchen Sie, new/delete anstelle von malloc/calloc als Operatoren zu verwenden. Für malloc/calloc müssen Sie einen weiteren Header einfügen. Mischen Sie nicht zwei verschiedene Sprachen im selben Code. Ihre Arbeit ist in jeder Hinsicht ähnlich. Beide weisen Speicher dynamisch aus dem Heap-Segment in der Hash-Tabelle zu.
Eine dynamische Zuordnung ist nur erforderlich, wenn die Lebensdauer des Objekts von dem Bereich abweicht, in dem es erstellt wird (dies gilt auch für das Verkleinern und Vergrößern des Bereichs), und wenn das Speichern nach Wert einen bestimmten Grund hat Arbeit.
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);
Ab C++ 11 haben wir std::unique_ptr
für den Umgang mit zugeordnetem Speicher, der den Besitz des zugeordneten Speichers enthält. std::shared_ptr
wurde erstellt, wenn Sie das Eigentum teilen müssen. (Sie werden dies weniger brauchen, als Sie in einem guten Programm erwarten würden)
Das Erstellen einer Instanz wird ganz einfach:
auto instance = std::make_unique<Class>(/*args*/); // C++14
auto instance = std::make_unique<Class>(new Class(/*args*/)); // C++11
auto instance = std::make_unique<Class[]>(42); // C++14
auto instance = std::make_unique<Class[]>(new Class[](42)); // C++11
C++ 17 fügt auch std::optional
, wodurch Sie möglicherweise keine Speicherzuweisungen benötigen
auto optInstance = std::optional<Class>{};
if (condition)
optInstance = Class{};
Sobald 'Instanz' den Gültigkeitsbereich verlässt, wird der Speicher aufgeräumt. Die Übertragung des Eigentums ist ebenfalls 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 noch new
? Fast nie mehr ab C++ 11. Die meisten von euch benutzen std::make_unique
, bis Sie zu einem Punkt gelangen, an dem Sie eine API aufrufen, die den Besitz über unformatierte 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 durchführen. Versuchen Sie in diesem Fall, ein Upgrade auf eine neuere Version des Standards durchzuführen. Wenn Sie nicht weiterkommen:
auto instance = new Class(); // Allocate memory
delete instance; // Deallocate
auto instances = new Class[42](); // Allocate memory
delete[] instances; // Deallocate
Vergewissern Sie sich, dass Sie den Besitz korrekt nachverfolgen, um keine Speicherverluste zu verursachen! Bewegungssemantik funktioniert auch noch nicht.
Wann brauchen wir malloc in C++? Der einzig gültige Grund wäre, Speicher zuzuweisen und ihn später durch Platzierung neu zu initialisieren.
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
Obwohl das oben Gesagte gültig ist, kann dies auch über einen neuen 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, in der Speicher im C++ - Code zugewiesen und im C-Code freigegeben wird (oder umgekehrt), müssen Sie malloc/free verwenden.
Wenn Sie in diesem Fall sind, vergessen Sie virtuelle Funktionen, Elementfunktionen, Klassen ... Es sind nur Strukturen mit PODs erlaubt.
Einige Ausnahmen zu den Regeln:
Wenn Sie C-Code haben, den Sie nach C++ portieren möchten, können Sie alle malloc () -Aufrufe darin belassen. Für jeden neuen C++ - Code würde ich empfehlen, stattdessen new zu verwenden.
new
initialisiert die Standardwerte der Struktur und verknüpft die darin enthaltenen Referenzen korrekt mit sich selbst.
Z.B.
struct test_s {
int some_strange_name = 1;
int &easy = some_strange_name;
}
Damit new struct test_s
gibt eine initialisierte Struktur mit einer funktionierenden Referenz zurück, während die malloced-Version keine Standardwerte hat und die internen Referenzen nicht initialisiert sind.
Aus einer niedrigeren Perspektive initialisiert new den gesamten Speicher, bevor der Speicher freigegeben wird, während malloc den ursprünglichen Speicherinhalt beibehält.
Die Verwendung von malloc/free anstelle von new/delete kann nur selten in Betracht gezogen werden, wenn Sie Realloc verwenden (einfache Pod-Typen, keine Objekte) und dann erneut zuweisen, da es in C++ keine ähnliche Funktion wie Realloc gibt (dies kann jedoch mit a erfolgen) mehr C++ Ansatz).
Die Operatoren new
und delete
können Klassen und Strukturen bearbeiten, während malloc
und free
nur mit Speicherblöcken arbeiten, die umgewandelt werden müssen.
Mit new/delete
verbessert Ihren Code, da Sie den zugewiesenen Speicher nicht in die erforderliche Datenstruktur umwandeln müssen.