new und malloc()



  • Hallo,

    Ich habe bisher nur in Java programmiert und bin daher mit OOP sehr gut vertraut, aber ich habe so meine Probleme mit der Speicherverwaltung. Um Speicher dynamisch zu allokieren verwendet man in C ja meist die Funktion malloc() und das Gegenstück free(). Das ist mir bis hierher ganz einleuchtend.

    In C++ kommen dann noch die Schlüsselwörter "new" und "delete" hinzu. Und schon gehen die Fragen los:

    Gibt es einen Unterschied zwischen "new" und malloc()? Wenn ja welchen? [gleiches gilt auch für "delete" und free()]

    Um noch mal zu C zurückzukommen; hier gibt es ja außerdem noch die Funktion realloc(). Gibt es hierfür auch eine Entsprechung in C++, oder kann man ganz normal realloc() verwenden? Sollte man in diesem Fall realloc() verwenden?

    hoffe ihr könnt mir hier weiterhelfen,
    Tobain



  • tobain schrieb:

    In C++ kommen dann noch die Schlüsselwörter "new" und "delete" hinzu.

    Die kommen nicht hinzu, sondern ersetzen malloc() und free().



  • im groben gesehen:
    new sorgt im gegensatz zu malloc fuer einen garantierten konstruktor aufruf von objekten. und fuer delete und free gilt das gleiche fuer die destruktoren.

    nein in c++ gibt es afaik kein aequivalentes realloc. dies laesst sich ja auch in den meisten faellen vermeiden.



  • Cpt. Tanga schrieb:

    new sorgt im gegensatz zu malloc fuer einen garantierten konstruktor aufruf von objekten.

    Also reserviert malloc nur den tatsächlichen Speicher, während "new Teller[10]" mir ein Array von Pointern für 10 Teller anlegt und für alle den Konstruktor Teller() aufruft, was natürlich auch bewirkt das irgendwo die 10 Teller auch noch allokiert werden. Was ist aber, wenn ich garnicht diesen Konstruktor aufrufen wollte, sondern für jedes Objekt einen andern?

    Cpt. Tanga schrieb:

    nein in c++ gibt es afaik kein aequivalentes realloc. dies laesst sich ja auch in den meisten faellen vermeiden.

    In Java wurde dies meist mit einer Kopie des kompletten Speicherbereiches bestraft, nur wie lässt es sich denn vermeiden? Ich bräuchte dazu mal ein Beispiel!

    mfG
    Tobain



  • tobain schrieb:

    Also reserviert malloc nur den tatsächlichen Speicher, während "new Teller[10]" mir ein Array von Pointern für 10 Teller anlegt und für alle den Konstruktor Teller() aufruft, was natürlich auch bewirkt das irgendwo die 10 Teller auch noch allokiert werden. Was ist aber, wenn ich garnicht diesen Konstruktor aufrufen wollte, sondern für jedes Objekt einen andern?

    Garnicht. Es werden auch nicht "10 Teller auch noch allokiert", sondern es werden nur 10 Teller allokiert. Praktisch wird das ein zusammenhängender Speicherblock der groß genug ist für 10 Teller. Dann wird auf jedes zehntel dieses Bereichs der Konstruktor aufgerufen (der konstruiert, der allokiert nicht).

    In Java wurde dies meist mit einer Kopie des kompletten Speicherbereiches bestraft, nur wie lässt es sich denn vermeiden? Ich bräuchte dazu mal ein Beispiel!

    Das hängt ganz von der verwendeten Speicherverwaltung ab. Mit new und delete musst Du tatsächlich einen neuen Bereich anlegen, den alten kopieren, und löschen. Ein STL-Vektor z.B. kann mithilfe der Speicherverwaltung der Standardbibliothek durchaus was effizienteres machen, wenn das denn möglich ist.



  • LordJaxom schrieb:

    Garnicht. Es werden auch nicht "10 Teller auch noch allokiert", sondern es werden nur 10 Teller allokiert. Praktisch wird das ein zusammenhängender Speicherblock der groß genug ist für 10 Teller. Dann wird auf jedes zehntel dieses Bereichs der Konstruktor aufgerufen (der konstruiert, der allokiert nicht).

    Teller* t = new Teller[10];
    

    Dieser Aufruf erzeugt also exakt einen Speicherbereich für 10 Teller, ruft den Konstruktor für jeden Teller auf und schreibt die Adresse des 1. Tellers in den Pointer.

    Kann man hier aber auch einen anderen Konstruktor angeben? Oder muß man dann selber nochmal den richtigen Konstruktor aufrufen? Wäre doch blöd wenn der Konstruktor etwas macht, was man garnicht möchte...

    LordJaxom schrieb:

    Das hängt ganz von der verwendeten Speicherverwaltung ab. Mit new und delete musst Du tatsächlich einen neuen Bereich anlegen, den alten kopieren, und löschen. Ein STL-Vektor z.B. kann mithilfe der Speicherverwaltung der Standardbibliothek durchaus was effizienteres machen, wenn das denn möglich ist.

    Spricht eigentlich etwas dagegen realloc() für diesen Zweck zu verwenden? Oder ist das Vermischen beider Konstrukte ehr nicht erlaubt/erwünscht?

    mfG
    Tobain



  • tobain schrieb:

    Kann man hier aber auch einen anderen Konstruktor angeben? Oder muß man dann selber nochmal den richtigen Konstruktor aufrufen? Wäre doch blöd wenn der Konstruktor etwas macht, was man garnicht möchte...

    Nein, das geht leider nur beim normalen new Operator, nicht beim new[] Operator.

    mfg.



  • Grundsätzlich darfst Du beide überhaupt nur mischen, wenn Du garantieren kannst, dass die Implementation der Standardbibliothek selbst malloc() für new und free() für delete verwendet. Das ist meistens so, ist aber keinesfalls Forderung des Standards.

    Und selbst dann würde ich die Finger davon lassen, da Du bei Veränderung der Speichergröße nicht nachhalten kannst, welche Objekte (de)konstruiert wurden und welche nicht.

    Einfaches Beispiel:

    PKW::Punto *flotte = new PKW::Punto[100];
    
    flotte = realloc(flotte, sizeof(PKW::Punto) * 50);
    
    delete[] flotte; // wird versuchen 100 Punto's zu zerstören, weil der C++-Speicherverwaltung keiner gesagt hat dass es nur noch 50 gibt
    


  • Hallo

    um für jedes einzelne Objekt in dem Array gleich beim ersten mal einen eigenen Konstruktoraufruf zu haben, kommst du nicht umhin ein statt einem Array aus Tellern ein Array von Pointern auf teller zu definieren. Dann kanst du nach dem Reservieren des Speichers des Arrays noch einzeln das Array iterieren und jedem einzelnem Element ein eigene Instanz mit angepassten Konstruktor zu übergeben.

    Dann bist du natürlich auch für das manuelle Löschen der einzelnen Instanzen verantwortlich.

    bis bald
    akari



  • LordJaxom schrieb:

    Und selbst dann würde ich die Finger davon lassen, da Du bei Veränderung der Speichergröße nicht nachhalten kannst, welche Objekte (de)konstruiert wurden und welche nicht.

    Danke, das ist wirklich einleuchtend, werde dann wohl doch einen weiten Bogen darum machen 🙂

    akari schrieb:

    um für jedes einzelne Objekt in dem Array gleich beim ersten mal einen eigenen Konstruktoraufruf zu haben, kommst du nicht umhin ein statt einem Array aus Tellern ein Array von Pointern auf teller zu definieren. Dann kanst du nach dem Reservieren des Speichers des Arrays noch einzeln das Array iterieren und jedem einzelnem Element ein eigene Instanz mit angepassten Konstruktor zu übergeben.

    Dann bist du natürlich auch für das manuelle Löschen der einzelnen Instanzen verantwortlich.

    Das ist dann wohl der Grund warum die STL so beliebt ist :p
    So kann man dann natürlich für jedes Objekt den entsprechenden Konstruktor aufrufen, muss dann aber leider sehr viele "new" Aufrufe durchführen. Dies dürfte doch recht aufwendig sein, da immer Häppchen für Häppchen allokiert wird...

    Momentan bin ich erst mal dabei das hier zu verdauen, melde mich dann wenn ich noch weitere Fragen habe. 👍



  • da faellt mir doch grad auch noch der placement new operator ein.
    weiss jemand ob folgendes standardkonform ist :

    T* ptr=new T[10];
    	ptr=new (&ptr) T[30];
    

    weiss jetzt nicht wie placement new reagiert wenn mehr speicher angefordert wird als vorhanden -aber vermutlich undefiniert? auch das loeschen duerfte dann wohl kritisch werden
    aber falls ja wuerde es doch einer art realloc gleich kommen.



  • edit (sollte eigentlich so aussehen):

    T* ptr=new T[10];
    	ptr=new (&ptr[0]) T[30];
    

  • Mod

    Cpt. Tanga schrieb:

    edit (sollte eigentlich so aussehen):

    T* ptr=new T[10];
    	ptr=new (&ptr[0]) T[30];
    

    placement new interessiert sich nicht dafür woher der speicher kommt - es wird niemals neuer speicher allokiert. schließlich kannst du placement new auch z.b. auf stackspeicher anwenden. problematischer ist hier dagegen (wenn wir jetzt mal den zu großen array index ignorieren), dass die Ts bereits konstruiert sind, bevor das placement new stattfindet. wenn T keinen trivialen destruktor hat, ist das ebenfalls undefiniertes verhalten. und ein delete [] auf ptr würde auf alle elemente des ursprünglichen arrays den destruktor aufrufen. als faustregel: wenn placement new T verwendet wird, sollte der benutzte speicher als roher speicher angefordert und zerstört werden (also malloc oder new char[] & co.).



  • tobain schrieb:

    Das ist dann wohl der Grund warum die STL so beliebt ist :p
    So kann man dann natürlich für jedes Objekt den entsprechenden Konstruktor aufrufen, muss dann aber leider sehr viele "new" Aufrufe durchführen. Dies dürfte doch recht aufwendig sein, da immer Häppchen für Häppchen allokiert wird...

    Als Abhilfe kannst du z.B. einen vector verwenden und diesen per reserve() genug Platz anfordern lassen für deine Daten:

    vector<Teller> data;
    //beschaffe genug ROHEN Speicher für zehn Teller:
    data.reserve(10);
    //fülle diesen Speicher mit zehn einzeln konstruierten Tellern:
    for(int i=0;i<10;++i)
      data.push_back(Teller(f(i),g(i));
    

    PS: Übrigens kann es durchaus sein, daß realloc auch alle Daten des alten Blocks kopiert.


Anmelden zum Antworten