wake-up-neo.com

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

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.

436
Ralph Burgess

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
347
Brian R. Bondy

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.

Durch new für C++ behobene Mängel

  1. 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
    }
    
  2. 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.

  3. 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;
    }
    
  4. 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();
    }
    
  5. 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!

134
Flexo

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. :)

51

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);
49
Ferruccio

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.

30
dmckee

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

25
Yogeesh H T

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

mallocreserviert Speicher, während newreserviert 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.

15

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
10

Es gibt einige Dinge, die new nicht malloc tun:

  1. new erstellt das Objekt, indem der Konstruktor dieses Objekts aufgerufen wird
  2. new erfordert keine Typumwandlung des zugewiesenen Speichers.
  3. Es ist keine Speicherzuweisung erforderlich, sondern es müssen mehrere Objekte erstellt werden.

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.

6
herohuyongtao

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.

4
PSkocik

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.

3
user3488100

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:

  • Sie schreiben eine Standardbibliothek mit erweiterten Datenstrukturen, in denen malloc angemessen ist
  • Sie müssen große Mengen an Speicher zuweisen (In Speicherkopie einer 10 GB-Datei?)
  • Sie verfügen über Werkzeuge, die die Verwendung bestimmter Konstrukte verhindern
  • Sie müssen einen unvollständigen Typ speichern
3
JVApen

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.

3
Fred Larson

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.

1
lama12345

Aus einer niedrigeren Perspektive initialisiert new den gesamten Speicher, bevor der Speicher freigegeben wird, während malloc den ursprünglichen Speicherinhalt beibehält.

1
Peiti Li

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).

0

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.

0
selwyn