Placement new, malloc und Alignment
-
Hallo,
Ich stehe gerade vor der Frage, ob ein placement new in einem mit malloc (oder einer anderen Allokations-Funktion) reservierten Speicherbereich zu Alignment-Problemen führen kann, vorallem wenn man in den allokierten Speicherbereich unterschiedliche Typen packen will. Relevant ist dabei vor allem etwaiger Overhead, der den ursprünglich allokierten Speicher übersteigt.Ich habe mal folgendes Beispiel erstellt, bei dem genug Speicher für 5 Objekt des Typs node und 3 Objekt des Typs obj allokiert wird und die Instanzen dann mit placement new in dem Speicherbereich erstellt werden:
#include <cstdlib> #include <new> #include <iostream> using namespace std; class node{ public: int m_prev; int m_next; node(int prev, int next): m_prev(prev), m_next(next){ } }; class obj{ public: int m_x; int m_y; int m_z; obj(int x, int y, int z): m_x(x), m_y(y), m_z(z){ } }; int main(int argc, char *argv[]){ int n = 3; int n_nodes = n + 2; char *buffer = reinterpret_cast<char*>( malloc(sizeof(node) * n_nodes + sizeof(obj) * n)); node* nodes = reinterpret_cast<node*>(buffer); obj* objs = reinterpret_cast<obj*>(nodes + n_nodes); node* theNode = nodes; obj* theObj = objs; for(int i = 0; i < n_nodes; ++i, ++theNode){ theNode = new(theNode) node(i - 1, i + 1); } for(int i = 0; i < n; ++i, ++theObj){ theObj = new(theObj) obj(i, i + 1, i + 2); } theNode = nodes; theObj = objs; cout << "nodes:" << endl; for(int i = 0; i < n_nodes; ++i, ++theNode){ cout << theNode->m_prev << " " << theNode->m_next << endl; } cout << "objs:" << endl; for(int i = 0; i < n; ++i, ++theObj){ cout << theObj->m_x << " " << theObj->m_y << " " << theObj->m_z << " " << endl; } return 0; }Das ganze funktioniert mit dem GCC problemlos - allerdings soll das ganze portabel und Standard konform sein - was bei solchen Sachen ja nicht immer ganz so einfach ist.
-
Ich würde
malloc()undnewbesser nicht mischen. Aber um rohen Speicher anzufordern, kannst du auchoperator newverwenden. Das hat die gleiche Schnittstelle wiemalloc(): einensize_t-Parameter für die Anzahl zu allokierender Bytes und als Rückgabetyp einenvoid*auf den angelegten Speicher:T* Memory = static_cast<T*>(operator new(sizeof(T) * NumberOfElements));Genauso kannst du Speicher freigeben, indem du
operator deletebenutzt. Aber denk dran, bei Klassentypen vorher den Destruktor explizit aufzurufen, oder gleichdeletezu verwenden.
-
Das Problem ist allerdings, das man manchmal auf irgendwelche System-spezifischen malloc-Funktionen angewiesen ist

-
maximAL schrieb:
Das Problem ist allerdings, das man manchmal auf irgendwelche System-spezifischen malloc-Funktionen angewiesen ist

Dann musst du die wrappen und verstecken, am Besten mit RAII, damit korrespondierende malloc() und free() Aufrufe sich nicht mit den new und delete-Aufrufen beißen.
-
pumuckl schrieb:
Dann musst du die wrappen und verstecken, am Besten mit RAII, damit korrespondierende malloc() und free() Aufrufe sich nicht mit den new und delete-Aufrufen beißen.
Ich glaube das Problem liegt eher darin, ob man Placement New auf mit
malloc()angeforderten Speicher anwenden dürfe. Der Speicher sollte ja aligned sein, sodass eine Konstruktion möglich ist. Aber wahrscheinlich gibt es wieder irgendwelche Einschränkungen, da bin ich mir gerade überhaupt nicht sicher...
-
Nexus schrieb:
Ich glaube das Problem liegt eher darin, ob man Placement New auf mit
malloc()angeforderten Speicher anwenden dürfe. Der Speicher sollte ja aligned sein, sodass eine Konstruktion möglich ist. Aber wahrscheinlich gibt es wieder irgendwelche Einschränkungen, da bin ich mir gerade überhaupt nicht sicher...malloc ist meines Wissens im Standard nicht erwähnt, außer als Bestandteil der C-Bibliotheken - und die garantieren afaik nicht das C++-Alignment. Ich denke der explizite Aufruf von operator new ist in dem Fall besser als malloc(). Andernfalls müsstest du außerdem später zum korrekten Abräumen der Objekte den Destruktor explizit aufrufen und danach free() - statt wie üblich einfach delete aufzurufen. Gerade für Arrays wäre das dann schon sehr nervig...
-
pumuckl schrieb:
Ich denke der explizite Aufruf von operator new ist in dem Fall besser als malloc().
Ja, das ist sicherer. Aber maximAL fragte ja wegen unvermeidbarem
malloc(). Ich hatte auch mal so ein Problem, als ich eine eigene Speicherverwaltung bastelte, dieoperator newüberladen hat. Da musste ich aufmalloc()zurückgreifen, und mir wurde gesagt, das sei okay. Link...pumuckl schrieb:
Gerade für Arrays wäre das dann schon sehr nervig...
Oh ja.

Wenn es irgendwie anders geht, würde ich sicher nicht so viel Low-Level einsetzen. Eine Möglichkeit stellen auch noch eigene Allokatoren dar.
-
Wäre da eventuell alignment_of von Nutzen?