Move-Konstruktor Aufruf durch std::list
-
Einen wunderschönen guten Morgen wünsche ich.
Leider wird dieser getrübt durch ein kleines Problemchen ;-). Ich möchte eine std::list anlegen, welche als Container für eine Klasse herhalten soll, die eine Ressource hält. Da diese Ressource im Destruktor dieser Klasse wieder freigegeben werden muss, kann ich mir keinen - inperformanten - Copy-Konstruktor leisten.#include <list> class Class { private: void* m_memory; public: Class(size_t size) : m_memory(new char[size]) {} Class(Class&& move) : m_memory(move.m_memory) { move.m_memory = nullptr; } ~Class() { if( m_memory ) { delete this->m_memory; } } }; int main() { std::list<Class> l; l.push_back(Class(10)); // 1. Call: Class::Class(size_t) // 2. Call: Class::~Class() return 0; }Ein ganz simples Beispiel. Ich würde mir wünschen, dass der Move-Konstruktor durch std::list<Class>::push_back() aufgerufen wird. Die Methode ist überladen:
std::list<Class>::push_back(Class const&); std::list<Class>::push_back(Class&&);Was genau mache ich falsch? Als Resultat erhalte ich Objekte in meiner std::list, welche ungültigen Speicher referenzieren.
Die Alternative wäre folgende Deklaration:std::list<Class*> l; l.push_back(new Class(10));Hier wird natürlich kein Destruktor aufgerufen, da es sich hier um kein temporäres Objekt handelt.
Ich halte diese Variante jedoch für ziemlichen Unsinn, da die Liste intern sowieso ihre "Nodes" durch den std::allocator<Class> - respektive einem eigenen STL-Allokator - allokiert.Für Hilfe wäre ich wirklich sehr dankbar.
-
Mach mal Zuweisungsoperator und Kopierkonstruktor private.
Ansonsten müßte das klappen.
Vielleicht gibt es ein emplace_back, das die &&-Überladung von push_back mit einem eigenen Namen ist.
-
Also...
- Je nach Version des Compilers, wird er entweder einen eigenen Kopierkonstruktor generieren oder nicht. Das solltest Du ausschließen. Die aktuellsten Regeln würden das verhindern. Aber Dein Compiler hält sich wahrscheinlich noch an ältere Regeln.
- Du hast vergessen, Dich um Zuweisungsoperatoren zu kümmern
- Du paarst
new[]für ein char-Array mitdeleteauf void*. Pfui. - Du kannst Dir die Klasse sparen; denn dafür ist unique_ptr da
#include <cstddef> #include <iostream> #include <ostream> #include <memory> #include <list> using namespace std; unique_ptr<char[]> make_char_array(std::size_t elements) { unique_ptr<char[]> p (new char[elements]); return p; } int main() { list<unique_ptr<char[]>> l; l.push_back(make_char_array(10)); l.front()[0] = 'x'; l.front()[1] = '\0'; cout << l.front().get() << endl; }Alternativ kannst Du einfach einen vector<char> benutzen. Der verhält sich eigentlich ähnlich und bietet darüber hinaus weitere Features an: Iteratoren, kann vergrößert werden, kann kopiert werden, kennt seine Größe. In C++0x werden Vektoren auch effizient "movable". Das heißt, all das, was Du hier mit unique_ptr machen würdest, kannst Du genauso effizient auch mit vector<char> machen.
kk
-
krümelkacker schrieb:
Du paarst
new[]für ein char-Array mitdeleteauf void*. Pfui.Danke für deine Antwort. Natürlich ist das nicht mein konkreter Anwendungsfall, sondern ein einfaches Beispiel. Mir ist klar, dass ein SmartPointer hier die richtige Wahl wäre, aber es ging doch einfach nur darum den Anwendungsfall zu demonstrieren.
Das ich hier nicht delete[] verwendet habe ist dem schnellen herunterschreiben dieses Beispiels geschuldet und nicht weiter ernst zu nehmen.