new und realloc
-
Frage: Was ist der Unterschied zwischen new und malloc?
Antwort: "new" ist ein Allocator, "malloc" ein anderer.
Auf vielen Systemen ruft new zwar bloss malloc auf, auf anderen aber nicht. Man kann und darf ruhig new und malloc "mischen", allerdings nicht "überkreuzen". Also new und dann free oder realloc auf den gleichen Ptr ist Blödsinn, falsch, Mist. In einem Programmteil new/delete und in einem anderen malloc/realloc/free ist dagegen kein Problem.
Ich weiss echt nicht was es da gross zu diskutieren oder zu erörtern gibt.
Ist im Prinzip wie die Frage was ist der Unterschied zwischen der Postbank und der Raiffeisenbank. Einen Credit den ich bei der Postbank genommen hab kann ich auch nicht bei der Raiffeisenbank zurückzahlen oder aufstocken lassen. Sollte eigentlich logisch sein...
Ob es auf dem einen oder anderen System geht, oder sogar auf den meisten, ist dabei vollkommen irrelevant.
-
so um noch mal auf mein ausgangsproblem zurückzukommen, hab ich das jetzt wie folgt gemacht und wollte wissen, ob das so geht bzw ob man das so machen sollte oder nicht.
class T{...}; ... int _size = 10; T** src; src = (T**) calloc (_size * sizeof(T)); if(!src) for(int i = 0; i < _size; i++) { src[i] = new T(); }; ...damit könnte ich dann quasi realloc anwenden um das feld dynamisch zu vergrößern, ohne das feld selber zwischen zu kopieren, und hab gleichzeitig kein 'problem' mit dem konstruktoren. dieser vorschlag wurde übrigens zu beginn schon mal angebracht, glaub ich zumindest. und ich hab ihn jetzt mal umgesetzt.
-
Theoretisch ist das schon so machbar, sofern du lediglich den Speicher hinter src mit realloc vergrösserst, und nicht den hinter src[...].
Aber warum nimmst du denn nicht einen passenden Container dafür? ZB std::vector. Der bietet ua die Funktionalität, bei Bedarf zu wachsen. Ausserdem sind dort idR Strategien implementiert, dass die Allokationsroutinen so sparsam wie möglich aufgerufen werden, und damit auch ein mögliches Umkopieren reduziert wird. Wenn du jegliches Umkopieren verhindern willst, dann nützt dir auch realloc nichts. Da müsstest du schon von Vornherein die Kapazität so gross wählen, dass dies für jede erdenkliche Situation ausreicht. Ausserdem ist ein Container exception-safe. Mit malloc/free und new/delete können da schnell Ressourcenlöcher entstehen.
-
FreakyBKA schrieb:
so um noch mal auf mein ausgangsproblem zurückzukommen, hab ich das jetzt wie folgt gemacht und wollte wissen, ob das so geht bzw ob man das so machen sollte oder nicht.
class T{...}; ... int _size = 10; T** src; src = (T**) calloc (_size * sizeof(T)); if(!src) for(int i = 0; i < _size; i++) { src[i] = new T(); }; ...damit könnte ich dann quasi realloc anwenden um das feld dynamisch zu vergrößern, ohne das feld selber zwischen zu kopieren, und hab gleichzeitig kein 'problem' mit dem konstruktoren. dieser vorschlag wurde übrigens zu beginn schon mal angebracht, glaub ich zumindest. und ich hab ihn jetzt mal umgesetzt.
Kann man so machen, wobei allerdings kein plausibler Grund zu sehen ist, warum src mit calloc angefordert wird und nicht ebenfalls mit new. ansonsten siehe groovemasters beiträge.
-
das calloc bzw malloc, hab ich deshalb verwendet um es nachträglich mit realloc vergrößern zu können ohne mir selber das array zwischenzuspeichern, das war doch gegenstand der ganzen diskussionen bisher, ob oder ob man nicht new/delete und malloc/free/realloc mischen sollte. hätte ich das mit new gemacht müsste ich mir selber eine funktion wie realloc halt nur für new schreiben.
und natürlich trenne ich die dimensionen des arrays, die eine wird nur mit malloc/calloc/free/realloc und die anderen nur mit new/delete behandelt.
ich habe deshalb keinen container, weil ich mir selbst eine array-klasse schreibe, wie zu anfang schonmal erwähnt.
-
Selbst das kann mächtig in die Hose gehen
realloc() ist afaik nicht verpflichtet, deine Daten an ihrer bisherigen Position stehen zu lassen (ist auch technisch gar nicht machbar) - und dann kopiert er binär und ohne den Copy-Ctor.(und wenn du bei einem C++ Objekt am Ctor vorbeiläufst, landest du im weiten Gebiet des undefinierten Verhaltens)
-
ich glaub du hast da was nicht richtig gelesen. es ist nur ein feld mit zeigern auf objekte und nur dieses wird vergrößert, d.h. die objekte an sich bleiben wo sie sind und bei realloc werden nur die zeiger kopiert (falls nötig), demnach brauch ich da keine konstruktoren.
-
FreakyBKA schrieb:
das calloc bzw malloc, hab ich deshalb verwendet um es nachträglich mit realloc vergrößern zu können ohne mir selber das array zwischenzuspeichern, das war doch gegenstand der ganzen diskussionen bisher, ob oder ob man nicht new/delete und malloc/free/realloc mischen sollte. hätte ich das mit new gemacht müsste ich mir selber eine funktion wie realloc halt nur für new schreiben.
und natürlich trenne ich die dimensionen des arrays, die eine wird nur mit malloc/calloc/free/realloc und die anderen nur mit new/delete behandelt.
ich habe deshalb keinen container, weil ich mir selbst eine array-klasse schreibe, wie zu anfang schonmal erwähnt.Ok, hatte ich mitlerweile vergessen. Wenn realloc verwendet wird, ist es aber sinnvoll, grundsätzlich nur realloc und nie eine der anderen Funktion (malloc/calloc/free) zu benutzen, damit wird die Intention wesentlich deutlicher. Der Begründung durch Bequemlichkeit ist allerdings eigentlich keine, denn wirklich bequem ist ja std::vector. Auch der theoretische Vorteil, dass realloc ohne Kopieren auskommen kann, ist näher besehen eine Fiktion. Entweder tut dein Programm sowieso nicht viel, dann spielt diese eingesparte Zeit keine Rolle. Tut dein Programm dagegen viel (auf dem Heap), dann ist es ohnehin extrem unwahrscheinlich, dass das Kopieren vermieden wird; da realloc keine Garantien (nicht mal im Mittel) gibt, ist diese Einsparung letzlich wertlos.
-
FreakyBKA schrieb:
ich habe deshalb keinen container, weil ich mir selbst eine array-klasse schreibe, wie zu anfang schonmal erwähnt.
OK, das ist natürlich was anderes. War mir mittlerweile auch entfallen. Asche auf mein Haupt. :xmas2:
Dann würde ich aber, ähnlich wie es die stdlib macht, die Allokation/Deallokation von der eigentlichen Klasse separieren. Soweit, dass man im ctor der Array Klasse für den Allokator eine Alternative verwenden kann, muss es ja nicht unbedingt gehen. Damit kannst du dann relativ einfach testen, welche Strategie für Allokation/Deallokation sich für deine Klasse in der Praxis bewährt.
Ich weiss, diese Idee mit den Zeigern auf die Elemente wurde am Anfang des Threads irgendwann mal gebracht. Grundsätzlich sollte man sowas aber mit Vorsicht geniessen. Selbst für eine Listen Klasse, die für sowas ja nahezu prädestiniert ist, ist das alles andere als optimal. Da kann man zB mit speziellen Allokatoren einiges gut machen. Das Problem ist einfach, dass dynamische Speicherreservierung recht aufwändig sein kann (abhängig von der Implementation). Und das für jedes Element kann je nach Szenario locker weitaus ineffizienter werden, als lineare Speicherreservierung. Zudem müllt es den Speichermanager zu. Ich würde deshalb, zumindest bei einer Array Klasse, eher lineare Speicherreservierung für die Elemente verwenden.
-
irgendwie versteh ich grad nicht so genua worauf du eigentlich hinaus willst.
-
int _size = 10; T** src; src = (T**) calloc (_size * sizeof(T)); if(!src) for(int i = 0; i < _size; i++) { src[i] = new T(); };Du reservierst hier jedes Element explizit. Ich würde eher auf folgender Basis aufbauen:
int _size = 10; T* src = new T[_size];Das ist nicht so Ressourcen intensiv, schont die Nerven des Speichermanagers und dürfte in vielen Lebenslagen performanter sein.
btw:
Verwende keine Bezeichner mit führenden Unterstrichen. Die sind eigentlich dem Compiler bzw. der stdlib vorbehalten. Wenn schon Unterstrich, dann nachführend.
-
irgendwie lest ihr teilweise nicht richtig oder habt schon wieder vergessen was die eigentliche fragestellung war. wenn ich das mit new() mache, dann sind wir doch wieder beim alten problem, falls ich das array nachträglich vergrößern will, weil ich dann kein realloc nutzen sollte.
sicher braucht man mehr speicher, aber halt nur für die zeiger.ich fasse also noch mal kurz zusammen:
- array-klasse über einen beliebigen typ (template)
- nachträgliches vergrößern (realloc)
- sicheres handhaben des typs, dh ggf. ctor aufrufen beim allozieren (new)da man wie wir bis jetzt festgestellt haben new und realloc nicht mischen sollte, bin ich jetzt über den weg mit dem feld von zeigern gegangen.
die unterstriche bei den variablen der klassen sind genau in anlehnung an die std gewählt, da ich mich wenigstens an irgendeinen standard halten möchte.
-
FreakyBKA schrieb:
irgendwie lest ihr teilweise nicht richtig oder habt schon wieder vergessen was die eigentliche fragestellung war. wenn ich das mit new() mache, dann sind wir doch wieder beim alten problem, falls ich das array nachträglich vergrößern will, weil ich dann kein realloc nutzen sollte.
Das war doch mehr oder weniger nur pseudo. Du kannst das natürlich auch mit malloc machen. Nur dann nicht vergessen, placement new zu verwenden, damit die Elemente korrekt initialisiert sind. Es ging einfach nur darum, den Container value-basiert zu gestalten, und nicht über diese Zeiger Indirektion. Zumindest bei Arrays halte ich das für sinnvoller.
FreakyBKA schrieb:
da man wie wir bis jetzt festgestellt haben new und realloc nicht mischen sollte, bin ich jetzt über den weg mit dem feld von zeigern gegangen.
Was aber nicht wirklich notwendig ist. Wer einen Container braucht, um von solch einem Layout zu profitieren, sucht wahrscheinlich etwas anderes als ein Array.
FreakyBKA schrieb:
die unterstriche bei den variablen der klassen sind genau in anlehnung an die std gewählt, da ich mich wenigstens an irgendeinen standard halten möchte.
Da hast du etwas falsch verstanden. In der stdlib, wohlgemerkt in der Implementation (im Standard wirst du davon nichts finden), werden diese Bezeichner verwendet, weil sie für Clientcode tabu sein sollten. Hatte letztens erst ein Projekt, wo jemand mehrere Bezeichner der Form __override verwendet hatte. Was ist passiert? Es liess sich unter Visual C++ 2005 nicht mehr kompilieren, weil in der Header <sal.h> bereits ein solcher Bezeichner vorhanden war. Und dann an Fremdcode rumzupfuschen, um es zum Laufen zu bringen, kann keine Lösung sein. Es daher besser, wie bereits erwähnt, in Clientcode nachführende Unterstriche zu verwenden, sofern man denn überhaupt welche benutzen will.
-
*push*
-
groovemaster schrieb:
FreakyBKA schrieb:
irgendwie lest ihr teilweise nicht richtig oder habt schon wieder vergessen was die eigentliche fragestellung war. wenn ich das mit new() mache, dann sind wir doch wieder beim alten problem, falls ich das array nachträglich vergrößern will, weil ich dann kein realloc nutzen sollte.
Das war doch mehr oder weniger nur pseudo. Du kannst das natürlich auch mit malloc machen. Nur dann nicht vergessen, placement new zu verwenden, damit die Elemente korrekt initialisiert sind. Es ging einfach nur darum, den Container value-basiert zu gestalten, und nicht über diese Zeiger Indirektion. Zumindest bei Arrays halte ich das für sinnvoller.
Eigenlich sind 7 Seiten über das Thema ja genug
Aber trotzdem ist seine Frage ja nicht beantwortet und mich interessiert ehrlich gesagt auch, was dir so als Lösung vorschwebt. Kannst du vielleicht ein kurzes Beispiel posten, wie dann die Array-vergrößerungs-Funktion aussehen würde? 
-
mit realloc (Quelle wurde mit malloc/realloc/calloc angefordert)
template<typename T> T* resize(T* p, size_t new_size) { T* q = static_cast< T* >( realloc( p, new_size * sizeof *p ) ); if ( q == NULL ) throw bad_alloc(); // p wurde nicht gelöscht! return q; }klar, einfach aber mit Einschränkungen:
T muss ein POD sein und darf kein alignment haben, dass größer ist als das des in dieser Hinsicht striktesten Standardtyps (Problem also z.B. bei 128bit SSE Datentypen).mit placement new
template<typename T> T* resize(T* p, size_t old_size, size_t new_size) { char* qq = new char[ new_size * sizeof *p ]; // wir können kein new T[.] verwenden, denn das würde bei nicht-PODs den Defaultkonstruktor aufrufen T* q = reinterpret_cast< T* >( qq ); // aufpassen beim delete, dass wir einen char* Pointer löschen! // alternativ kann nat. auch eine andere Allokationsfunktion mit geeigneten alignmentgarantien verwendet werden size_t i = 0; try { for ( ; i < min( old_size, new_size ); ++i ) new( q + i ) T( p[ i ] ); } catch ( ... ) { for ( ; i != 0; ) q[ --i ].~T(); delete [] qq; throw; } for ( i = old_size; i != 0; } p[ --i ].~T(); delete [] reinterpret_cast< const char* >( p ); return q; }Nicht mehr auf PODs beschränkt, aber wegen der char*/T* problematik geringfügig unhandlich. Hinsichtlich alignments so problematisch wie die zugrundeliegende Allokationsfunktion (kein Problem mit new und Standardtypen).
Das ganze mit uninitialized_copy:
template<typename T> T* resize(T* p, size_t old_size, size_t new_size) { char* qq = new char[ new_size * sizeof *p ]; T* q = reinterpret_cast< T* >( qq ); try { uninitialized_copy( p, p + min( old_size, new_size ), q ); } catch ( ... ) { delete [] qq; throw; } for ( i = old_size; i != 0; } p[ --i ].~T(); delete [] reinterpret_cast< const char* >( p ); return q; }mit Allokator
template< typename T, template< typename > class A > T* resize(T* p, size_t old_size, size_t new_size) { A< T > alloc; T* q = alloc.allocate( new_size, p ); try { uninitialized_copy( p, p + min( old_size, new_size ), q ); } catch ( ... ) { alloc.deallocate( q ); throw; } for ( size_t i = old_size; i != 0; ) alloc.destroy( p + --i ); alloc.deallocate( p ); return q; }Das dürfte die leistungsfähigste Variante sein, keine Einschränkungen hinsichtlich PODs oder alignment (sofern entsprechende Allokatoren existieren), und die Allokationsstrategie kann flexibel optimiert werden.
P.S. old_size ist jeweils die Anzahl der initialisierten Elemente im ursprünglichen Array, nicht notwendigerweise dessen Größe
-
@camper:
entwickelst du selber compiler oder woher kommt dein extrem gutes fachwissen?
:xmas2:
-
ok, damit dürfte alles geklärt sein^^
Aber hab noch eine Frage zu dem Code:
new( q + i ) T( p[ i ] );Gibt man da mit q+i die Speicherstelle an, wo das Objekt erstellt werden soll?
-

was ist POD
was ist alignment
gehts auch noch komplizierter? :p
gehts auch so?template <class t> inline void carray<t>::set_size(int size) { if (size == _size) return; try { _data = (t**) realloc (_data, size * sizeof(t*)); if (size && !_data) throw memory_exception(0); _upper_bound = min(size - 1, _upper_bound); for(int i = _size; i < size; i++) _data[i] = 0; _size = size; } catch(cexception e) { _size = 0; _upper_bound = -1; cout << e; }; };cexception, memory_exception: fehlerklassen von mir
_data: array mit den zeigern
_size: größe des allozierten array
_upper_bound: größter zulässiger index (kann auch kleiner als _size-1 sein)wenn das so nicht geht, wüsste ich gern wo genau die probleme liegen bzw welchen einschränkungen diese version unterliegt, da sie bis jetzt funktioniert.
-
POD - keine Ahnung^^
Alignment - IMO wird der Speicher den du dir holst, auf eine gerade Bytezahl gestellt. D.h. wenn du dir 1 Byte holst (malloc/realloc/new), ist der Speicher dafür je nach System 4, 8, 16 Bytes groß oder so.
Das Problem bei deinem Code ist, dass, wenn der Speicher verkleinert wird, der Destruktor für die entfallenen Objekte nicht aufgerufen wird. Für mehr Objekte wird der Konstruktor für die neuen nicht aufgerufen. Und wenn die Objekte "verschoben" werden, weil realloc Speicher an einer anderen Stelle zurückgibt, kriegen die Objekte nix davon mit, dass sie woanders liegen, was zu Problemen führen kann.
Bei campers (abgefahrenem) Code werden size-Anzahl Objekte neu erzeugt, für die wird der Copy-Konstruktor mit Referenz auf das alte Objekt aufgerufen. Die alten werden dann dekonstrukturiert.