Warum
-
realloclässt sich nicht sinnvoll für Non-PODs benutzen, weil besitzende Objekte nicht einfach im Speicher verschoben werden dürfen. Man bräuchte also eine andere Funktion für die Speicheranforderung. Zumindest so etwas wietry_realloc, um das genannte Problem zu verhindern.*bla bla über Für und Wider auslass*
-
TyRoXx schrieb:
realloclässt sich nicht sinnvoll für Non-PODs benutzen, weil besitzende Objekte nicht einfach im Speicher verschoben werden dürfen.Laut deiner Signatur hast du schon mal von std::vector gehört. Fragen über Fragen ...

-
warum123456 schrieb:
Warum hat sich das C++ Komitee dagegen entschieden ein realloc Äquivalent zu [bieten]?
Wahrscheinlich, weil's der Aufwand nicht wert wäre. Also, new[] und delete[] sind ja eh schon fast sinnfrei, weil wir std::vector haben. Und std::vector arbeitet mit Allokatoren (allocate, deallocate, construct, destroy, ...).
Ich würde auch behaupten, dass der C++ Standard es einer std::vector-Implementierung nicht verbietet, im Falle des Standard-Allocators etwas "schlauer" zu sein. Beispielsweise kann ich mir eine Implementierung vorstellen, die im Falle von PODs tatsächlich auf realloc zurückgreift. Im Falle von non-PODs bräuchte man noch so etwas wie ein try_realloc, wie schon genannt, weil man diese Objekte nicht per memcpy verschieben kann. Wenn der Speicherblock nicht vergrößert werden kann und ein neuer her muss, müssen die Elemente einfach Objekt für Objekt per Kopierkonstruktor oder Movekonstruktor (falls T "noexcept movable" ist) im neuen Speicherblock konstruiert werden und im alten wieder zerstört werden.
Das sind Dinge, die man als Hersteller einfach so als QoI-Feature einbauen könnte, ohne dass man da im Standard weiter darauf eingehen muss. Du kannst Dich ja mal dafür beim Hersteller Deines Vertrauens einsetzen.

-
Ich muss jetzt mal ganzdumm fragen... Warum sollte man Non-PODs nicht mit memcpy oder realloc verschieben können?
Der einzige Grund, der mir einfällt wäre hier, dass die Objekte nach dem realloc nicht mehr so im Speicher "aligned" sind, wie der Compiler das eigentlich geplant hatte.
Abgesehen vom Alignment sollte das hier z.B. locker klappen:
#include <string> std::string * strs = static_cast<std::string *>(malloc(sizeof(std::string) * 10)); for (int n = 0; n < 10; ++n) new (&strs[n]) std::string(); // Vergrößern strs = static_cast<std::string *>(realloc(static_cast<void*>(strs), sizeof(std::string) * 15)); for (int n = 10; n < 15; ++n) new (&strs[n]) std::string(); // Verkleinern for (int n = 12; n < 15; ++n) strs[n].~string(); // ka, ob das die korrekte Syntax ist... sowas packe ich normalerweise in einer Templatefunktion, da klappt das :-) strs = static_cast<std::string *>(realloc(static_cast<void*>(strs), sizeof(std::string) * 12)); // Freigeben for (int n = 0; n < 12; ++n) strs[n].~string(); // selbes wie oben free(strs); // Man muss bei der ganzen Sache eben irgendwo speichern, wie viele Instanzen des Non-PODs man hat und man muss manuell Konstruktoren und Destruktoren aufrufen.Abgesehen vom Alignment sind Non-PODs auch nur eine Reihe von Bytes, die man im Speicher rumschieben kann. So lange Konstruktoren und Destruktoren immer mit dem aktuellen this-Zeiger versorgt werden, sollte es da keine Probleme geben.
ich kann natürlich kein memcpy mit std::string machen und dann beide freigeben, da ja beide den gleichen Zeiger intern speichern. Aber das sollte ja klar sein
Sollte ich völlig falsch liegen, würde ich mich mal über Aufklärung freuen.
-
krümelkacker
Warum sollte man die Objekte nicht im Speicher verschieben können? Und seit wann macht std::vector etwas anderes wenn er neu reservieren muss?
-
Falsch. Du denkst viel zu restriktiv, was non-POD alles bedeuten kann. Selbst für den relativ einfach gestrickten std::string bringst du in deinem eigenem Beitrag allerlei Einschränkungen und Sachen auf die man aufpassen muss. Wie soll das bei ganz allgemeinen Non-PODs funktionieren?
-
DrakoXP schrieb:
Der einzige Grund, der mir einfällt wäre hier, dass die Objekte nach dem realloc nicht mehr so im Speicher "aligned" sind, wie der Compiler das eigentlich geplant hatte.
Interne Zeiger.
-
DrakoXP schrieb:
Ich muss jetzt mal ganzdumm fragen... Warum sollte man Non-PODs nicht mit memcpy oder realloc verschieben können?
Weil es im Allgemeinen keine einfachen Datensammlungen mehr sind, sondern Objekte, die selbst wissen wollen, wann & wo sie erzeugt und zerstört werden. Beispielsweise könnte ich eine Klasse bauen, deren Objekte sich mit ihrem this-Zeiger irgendwo registrieren, so lange sie leben. Das würdest Du damit völlig kaputt machen.
DrakoXP schrieb:
Abgesehen vom Alignment sind Non-PODs auch nur eine Reihe von Bytes, die man im Speicher rumschieben kann.
Nö!
Probier das mal beispielsweise bei linked_ptr<>, wo sich Objekt-Kopien selbst in eine verkettete Liste einreihen. Das geht gehörig schief.
Sicher gibt es auch non-POD-Klassen, die man prinzipiell per memcpy "moven" könnte, ohne dass etwas schief läuft. Aber verlassen kannst Du Dich da nicht drauf, dass ein bestimmter Typ T das erlaubt.
warum123456 schrieb:
@krümelkacker:
Warum sollte man die Objekte nicht im Speicher verschieben können?Habe ich glaub'ich inzwischen beantwortet.
warum123456 schrieb:
Und seit wann macht std::vector etwas anderes wenn er neu reservieren muss?
std::vector verwendet kein memcpy/realloc, um den Kram zu "verschieben", sondern geht über den Kopierkonstruktor bzw Movekonstruktor des Elementtyps und zerstört ordnungsgemäß die Objekte, die im alten Speicherblock lebten. Zumindest bei non-PODs.
Wie gesagt, ich kann mir vorstellen, dass ein Compilerhersteller für vector<T> mit T als POD eine Spezialisierung baut, die im Endeffekt realloc verwendet. Für non-PODs ist realloc zu brutal, weil es den Kram kopiert und den alten Speicher zu früh freigibt, ohne dass man die Gelegenheit bekommt, die Objekte per Copy-Ctor bzw Move-Ctor umziehen zu lassen und die alten danach noch zu destruieren. Der Speicher ist ja schon futsch dann.
-
Bashar schrieb:
DrakoXP schrieb:
Der einzige Grund, der mir einfällt wäre hier, dass die Objekte nach dem realloc nicht mehr so im Speicher "aligned" sind, wie der Compiler das eigentlich geplant hatte.
Interne Zeiger.
Die werden ja im Beispiel(!) durchs placement-new durchaus befriedigt.
-
So ein "renew" könnte doch die move Konstruktoren nutzen?
-
warum123456 schrieb:
So ein "renew" könnte doch die move Konstruktoren nutzen?
Du brauchst nur eine Art "try_realloc", was den Speicherblock zu vergrößern versucht aber nichts macht, wenn es nicht klappt. Den Rest kannst Du dann in der std::vector<T> Implementierung lösen, ohne dass man am C++ Standard etwas ändern muss.
Wie gesagt, new[] & delete[] ist quasi für'n 4rsch. Ich sehe absolut keinen Sinn darin, ein renew[] mit in die Sprache aufzunehmen. Aber du kannst es ja vorschlagen, wenn Du es haben willst. Mir persönlich fallen da andere Dinge ein, die wichtiger sind.