597 Stimmen

In welchen Fällen sollte ich malloc und/oder new verwenden?

Ich sehe, dass es in C++ mehrere Möglichkeiten gibt, Daten zuzuweisen und freizugeben, und ich verstehe, dass Sie beim Aufruf von malloc sollten Sie anrufen free und wenn Sie die new Operator sollten Sie koppeln mit delete und es ist ein Fehler, die beiden zu vermischen (z.B. Aufruf von free() auf etwas, das mit dem Programm new Operator), aber ich bin mir nicht sicher, wann ich die malloc / free und wann sollte ich die new / delete in meinen Programmen der realen Welt.

Wenn Sie ein C++-Experte sind, lassen Sie mich bitte wissen, welche Faustregeln oder Konventionen Sie in dieser Hinsicht befolgen.

35voto

Um Ihre Frage zu beantworten, sollten Sie wissen der Unterschied zwischen malloc y new . Der Unterschied ist einfach:

malloc weist Speicher zu , während new weist Speicher zu UND ruft den Konstruktor auf des Objekts, für das Sie Speicher zuweisen.

Wenn Sie also nicht auf C beschränkt sind, sollten Sie niemals malloc verwenden, insbesondere wenn Sie mit C++-Objekten arbeiten. Das wäre ein Rezept, um Ihr Programm zu zerstören.

Auch der Unterschied zwischen free y delete ist genau dasselbe. Der Unterschied ist, dass delete ruft den Destruktor Ihres Objekts auf und gibt zusätzlich Speicher frei.

34voto

使用方法 malloc y free seulement für die Zuweisung von Speicher, der von c-zentrischen Bibliotheken und APIs verwaltet werden soll. verwenden new y delete (und die [] Varianten) für alles, was Sie kontrollieren.

16voto

JVApen Punkte 10587

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

16voto

R. Martinho Fernandes Punkte 217895

Es gibt einen großen Unterschied zwischen malloc y new . malloc weist Speicher zu. Für C ist das in Ordnung, denn in C ist ein Klumpen Speicher ein Objekt.

In C++ müssen Sie, wenn Sie es nicht mit POD-Typen zu tun haben (die den C-Typen ähnlich sind), einen Konstruktor für einen Speicherplatz aufrufen, um dort tatsächlich ein Objekt zu haben. Nicht-POD-Typen sind in C++ sehr verbreitet, da viele C++-Funktionen ein Objekt automatisch zu einem Nicht-POD-Typ machen.

new weist Speicher zu y erzeugt ein Objekt an dieser Speicherstelle. Bei Nicht-POD-Typen bedeutet dies den Aufruf eines Konstruktors.

Wenn Sie so etwas tun:

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 zeigt. Sie müssen einen Konstruktor für ihn aufrufen, bevor Sie ihn verwenden können (und das geschieht durch die Platzierung new ).

Wenn Sie es hingegen tun:

non_pod_type* p = new non_pod_type();

Sie erhalten einen Zeiger, der immer gültig ist, weil new ein Objekt erstellt.

Selbst bei den POD-Typen gibt es einen erheblichen Unterschied zwischen den beiden:

pod_type* p = (pod_type*) malloc(sizeof *p);
std::cout << p->foo;

Dieses Stück Code würde einen nicht spezifizierten Wert ausgeben, da die POD-Objekte, die von malloc werden nicht initialisiert.

new können Sie einen Konstruktor angeben, der aufgerufen wird, 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 verwenden new um nicht-initialisierte POD-Objekte zu erhalten. Siehe diese andere Antwort für weitere Informationen zu diesem Thema.

Ein weiterer Unterschied ist das Verhalten bei Misserfolg. Wenn es nicht gelingt, Speicher zuzuweisen, malloc gibt einen Null-Zeiger zurück, während new eine Ausnahme auslöst.

Bei der ersten Variante müssen Sie jeden zurückgegebenen Zeiger testen, bevor Sie ihn verwenden, während die zweite Variante immer gültige Zeiger erzeugt.

Aus diesen Gründen sollten Sie in C++-Code new und nicht malloc . Aber auch dann sollten Sie nicht new "in the open", weil es Ressourcen erwirbt, die Sie später wieder freigeben müssen. Wenn Sie new sollten Sie das Ergebnis sofort an eine ressourcenverwaltende Klasse weitergeben:

std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won't leak

7voto

herohuyongtao Punkte 47389

Es gibt ein paar Dinge, die new ist das malloc nicht:

  1. new konstruiert das Objekt durch Aufruf des Konstruktors dieses Objekts
  2. new erfordert kein Typecasting des zugewiesenen Speichers.
  3. Es muss nicht eine bestimmte Menge an Speicher zugewiesen werden, sondern eine Reihe von Objekte, die konstruiert werden müssen.

Wenn Sie also malloc dann müssen Sie die oben genannten Dinge explizit tun, was nicht immer praktisch ist. Zusätzlich, new kann überlastet werden, aber malloc kann nicht sein.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X