c++ - new - malloc qt




In welchen Fällen verwende ich malloc vs new? (12)

Ich sehe in C ++, es gibt mehrere Möglichkeiten, um Daten zu verteilen und freizugeben, und ich verstehe, dass wenn Sie malloc aufrufen, sollten Sie free anrufen und wenn Sie den new Operator verwenden, sollten Sie mit delete und es ist ein Fehler, die beiden zu mischen free() auf etwas, das mit dem new Operator erstellt wurde, aber mir ist nicht klar, wann ich malloc / free und wann ich new / delete in meinen realen Programmen verwenden soll.

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


Aus dem C ++ FQA Lite :

[16.4] Warum sollte ich neue anstelle von vertrauenswürdigen alten malloc () verwenden?

FAQ: new / delete Aufruf des Konstruktors / Destruktors; neu ist typsicher, malloc ist nicht; Neu kann von einer Klasse überschrieben werden.

FQA: Die in den FAQ erwähnten Tugenden von new sind keine Tugenden, da Konstruktoren, Destruktoren und das Überladen von Operatoren Müll sind (was passiert, wenn Sie keine Speicherbereinigung haben?), Und das Typsicherheitsproblem ist hier wirklich winzig um die von malloc an den rechten Zeigertyp zurückgegebene Lücke * umzuwandeln, um sie einer typisierten Zeigervariablen zuzuweisen, was zwar ärgerlich sein kann, aber weit von "unsicher" entfernt ist.

Oh, und mit vertrauenswürdigen alten malloc macht es möglich, die gleichermaßen vertrauenswürdig und alt realloc zu verwenden. Schade, wir haben keine glänzende neue Betreiber erneuern oder so etwas.

Dennoch ist Neu nicht schlecht genug, um eine Abweichung vom üblichen Stil einer Sprache zu rechtfertigen, selbst wenn die Sprache C ++ ist. Insbesondere Klassen mit nicht-trivialen Konstruktoren werden sich auf fatale Weise falsch verhalten, wenn Sie die Objekte einfach mallocieren. Warum also nicht im gesamten Code neu verwenden? Leute überladen den Operator selten neu, so dass er Ihnen wahrscheinlich nicht zu sehr in die Quere kommt. Und wenn sie neu überladen, können Sie sie immer bitten zu stoppen.

Entschuldigung, ich konnte einfach nicht widerstehen. :)


Aus einer niedrigeren Perspektive initialisiert new den gesamten Speicher, bevor er den Speicher gibt, während malloc den ursprünglichen Inhalt des Speichers behält.


Die kurze Antwort ist: Verwenden Sie malloc für C ++ nicht ohne einen wirklich guten Grund dafür. malloc weist eine Reihe von Mängeln auf, wenn es mit C ++ verwendet wird.

Fehler behoben durch Neu für C ++ Code

  1. malloc ist in keiner sinnvollen Weise typsicher. In C ++ müssen Sie die Rückgabe von void* . Dies führt möglicherweise zu vielen 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 es sich bei dem betreffenden Typ um POD (einfache alte Daten) handelt , können Sie malloc semi-vernünftig verwenden, um malloc zu reservieren, wie es im ersten Beispiel für f2 Fall ist.

    Es ist jedoch nicht so offensichtlich, wenn ein Typ POD ist. Die Tatsache, dass es für einen gegebenen Typ möglich ist, von POD zu Nicht-POD zu wechseln, ohne dass ein Compiler-Fehler auftritt und möglicherweise sehr schwer zu debuggen ist, ist ein wesentlicher Faktor. Zum Beispiel, wenn jemand (möglicherweise ein anderer Programmierer, während der Wartung, viel später eine Änderung vornehmen würde, die foo nicht mehr POD sein würde, dann würde kein offensichtlicher Fehler zur Kompilierzeit erscheinen, wie du es hoffen würdest, zB:

    struct foo {
      double d[5];
      virtual ~foo() { }
    };
    

    würde das malloc von f2 auch schlecht werden lassen, ohne irgendeine offensichtliche Diagnose. Das Beispiel hier ist trivial, aber es ist möglich, versehentlich Nicht-PODness viel weiter weg einzuführen (zB in einer Basisklasse, indem ein Nicht-POD-Mitglied hinzugefügt wird). Wenn Sie C ++ 11 / boost haben, können Sie mit is_pod überprüfen, ob diese Annahme korrekt ist und einen Fehler erzeugen, wenn dies nicht der is_pod 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)));
    }
    

    Boost kann zwar nicht feststellen, ob ein Typ POD ohne C ++ 11 oder einige andere Compiler-Erweiterungen ist.

  3. malloc gibt NULL wenn die Zuweisung fehlschlägt. new wird std::bad_alloc werfen. Das Verhalten später mit einem Nullzeiger ist nicht definiert. Eine Ausnahme hat eine saubere Semantik, wenn sie ausgelöst wird und von der Fehlerquelle ausgelöst wird. Das Verpacken von malloc mit einem geeigneten Test bei jedem Aufruf erscheint mühsam und fehleranfällig. (Sie müssen nur einmal vergessen, all diese gute Arbeit rückgängig zu machen). Eine Exception kann auf eine Ebene propagiert werden, auf der ein Aufrufer sie sinnvoll verarbeiten kann, wobei es NULL schwerer fällt, sinnvoll zurück zu geben. Wir könnten unsere safe_foo_malloc Funktion erweitern, um eine Ausnahme safe_foo_malloc oder das Programm zu safe_foo_malloc 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 ist ein C ++ - Feature. Als Ergebnis spielt malloc nicht gut mit Konstruktoren zusammen, es wird nur nach einem Byte-Anteil gesucht. Wir könnten unser safe_foo_malloc weiter safe_foo_malloc , um das 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. Unsere safe_foo_malloc Funktion ist nicht sehr generisch - im Idealfall würden wir etwas wollen, das mit jedem Typ umgehen kann, nicht nur mit foo . Dies können wir mit Templates und variadischen Templates für Nicht-Standard-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)...);
      }
    };
    

    Obwohl wir alle bisher identifizierten Probleme behoben haben, haben wir den new Standardoperator praktisch neu erfunden. Wenn Sie malloc und placement new verwenden malloc Sie auch einfach neu verwenden!


Ein seltener Fall, bei dem malloc / free anstelle von new / delete in Erwägung gezogen wird, ist das Zuweisen und erneute Zuordnen (einfache Pod-Typen, keine Objekte) mit realloc, da es keine ähnliche Funktion zum erneuten Zuweisen in c ++ gibt (obwohl dies mit einem mehr c ++ möglich ist) Ansatz)


Es gibt einen großen Unterschied zwischen malloc und new . malloc weist Speicher zu. Das ist gut für C, denn in C ist ein Stück Speicher ein Objekt.

Wenn Sie in C ++ nicht mit POD-Typen arbeiten (die C-Typen ähneln), müssen Sie einen Konstruktor auf einem Speicherort aufrufen, um dort tatsächlich ein Objekt zu haben. Nicht-POD-Typen sind in C ++ sehr häufig, da viele C ++ - Funktionen ein Objekt automatisch zu einem Nicht-POD machen.

new weist Speicher zu und erstellt ein Objekt an diesem Speicherort. Für Nicht-POD-Typen bedeutet dies, einen Konstruktor aufzurufen.

Wenn Sie so etwas tun:

non_pod_type* p = (non_pod_type*) malloc(sizeof *p);

Der von Ihnen abgerufene Zeiger kann nicht dereferenziert werden, da er nicht auf ein Objekt zeigt. Sie müssten zuerst einen Konstruktor aufrufen, bevor Sie ihn verwenden können (und das geschieht mit placement new ).

Wenn Sie andererseits:

non_pod_type* p = new non_pod_type();

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

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 Codeabschnitt würde einen nicht angegebenen Wert ausgeben, da die von malloc erstellten POD-Objekte nicht initialisiert werden.

Mit new können Sie einen Konstruktor angeben, der aufgerufen werden soll, und somit einen gut definierten Wert erhalten.

pod_type* p = new pod_type();
std::cout << p->foo; // prints 0

Wenn Sie es wirklich wollen, können Sie mit new nicht initialisierte POD-Objekte erhalten. Weitere Informationen hierzu finden Sie in dieser anderen Antwort .

Ein weiterer Unterschied ist das Verhalten bei Versagen. Wenn Speicher nicht zugewiesen werden kann, gibt malloc einen Nullzeiger zurück, während new eine Ausnahme auslöst.

Bei der ersten Methode müssen Sie jeden zurückgegebenen Zeiger testen, bevor Sie ihn verwenden, während bei der letzteren immer gültige Zeiger ausgegeben werden.

Aus diesen Gründen sollten Sie in C ++ Code new und nicht malloc . Aber selbst dann sollten Sie kein new "im Freien" verwenden, da es Ressourcen benötigt, die Sie später freigeben müssen. Wenn Sie new , sollten Sie das Ergebnis sofort an eine Ressourcenverwaltungsklasse übergeben:

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

Im folgenden Szenario können wir nicht new verwenden, da es Konstruktor aufruft.

class  B  {
private:
    B *ptr;
    int x;
public:
    B(int n)  {
        cout<<"B: ctr"<<endl;
        //ptr = new B;  //keep calling ctr, result is segmentation fault
        ptr = (B *)malloc(sizeof(B));
        x = n;
        ptr->x = n + 10;
    }
    ~B()  {
        //delete ptr;
        free(ptr);
        cout<<"B: dtr"<<endl;
    }
};

Verwenden Sie malloc und free nur zum Zuweisen von Arbeitsspeicher, der von c-zentrischen Bibliotheken und APIs verwaltet wird. Verwenden Sie new und delete (und die [] Varianten) für alles, was Sie steuern.


Verwenden Sie immer neu in C ++. Wenn Sie einen Block nicht typisierten Speichers benötigen, können Sie den Operator new direkt verwenden:

void *p = operator new(size);
   ...
operator delete(p);

Wenn Sie C ++ verwenden, dann versuchen Sie, new / delete anstelle von malloc / calloc zu verwenden, da sie im Vergleich zu malloc / calloc für sie selbst einen anderen Header enthalten. Also mischen Sie nicht zwei verschiedene Sprachen in einer einzigen Kodierung Wenn ihre Arbeit in jeder Hinsicht ähnlich ist, weist sie den Speicher dynamisch dem Heap-Segment in der Hash-Tabelle zu.


Wenn Sie mit Daten arbeiten, die keine Konstruktion / Zerstörung benötigen und Neuzuweisungen erfordern (z. B. eine große Anzahl von Ints), dann ist malloc / free meiner Meinung nach eine gute Wahl, da es Ihnen realloc gibt, was viel schneller ist als new-memcpy -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 sehe nicht, warum Sie nicht beide verwenden sollten (vorausgesetzt, dass Sie Ihren Speicher freigeben und Objekte löschen, die mit neuen zugewiesen wurden), wenn Sie die Geschwindigkeitsverstärkung nutzen können (manchmal eine signifikante, wenn Sie große Arrays neu zuweisen) von POD), die Realloc Ihnen geben kann.

Wenn Sie es nicht benötigen, sollten Sie sich in C ++ an Neuem / Löschen halten.


malloc () wird verwendet, um Speicher in C dynamisch zuzuweisen, während die gleiche Arbeit von new () in C ++ ausgeführt wird. Sie können also keine Kodierungskonventionen von zwei Sprachen mischen. Es wäre gut, wenn Sie nach dem Unterschied zwischen Calloc und Malloc () fragen würden.


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;
}

Die new struct test_s gibt also eine initialisierte Struktur mit einer Arbeitsreferenz zurück, während die Malloc-Version keine Standardwerte hat und die internen Referenzen nicht initialisiert werden.





new-operator