Problem mit eigener Vectorklasse
-
Das folgende minimale Beispiel:
#include <algorithm> template<class T> class vector { T* ptr; unsigned sz; public: vector(unsigned n, const T& fill) : ptr(new T[n]), sz(n) { std::fill(ptr, ptr + sz, fill); } }; struct foo { foo(int) { } }; int main() { vector<foo> bar(5, 2); }
Wie kann ich den Konstruktor mit n und mit fill fixen, sodass er so funktioniert wie erwartet und den Nicht-Standard-Konstruktor von foo aufruft?
-
Wie kann ich den Konstruktor mit n und mit fill fixen, sodass er so funktioniert wie erwartet und den Nicht-Standard-Konstruktor von foo aufruft?
Indem du zuerst nur den Speicher allokierst und dann nacheinander mit placement-new durchgehst und jedes Element einzeln konstruierst.
-
Grobes Beispiel:
#include <algorithm> template<class T> class vector { T* ptr; std::size_t sz; /// Sollte wohl eher size_t sein public: static_assert( std::is_nothrow_destructible<T>::value, "" ); /// Definiert automatisch Kopierkonstruktor und -zuweisungsoperator als deleted. vector( vector&& ) = default; vector& operator=( vector&& ) = default; vector(std::size_t n, const T& fill) : ptr( static_cast<T*>(operator new(n * sizeof(T))) ), sz(n) { for( std::size_t i = 0; i != sz; ++i ) try { new (ptr + i) T(fill); } catch( ... ) { for( auto ic = i; ic--; ) (ptr + ic)->~T(); operator delete(ptr); throw; } } ~vector() { for( std::size_t i = 0; i != sz; ++i ) ptr[i].~T(); operator delete(ptr); } }; #include <iostream> struct foo { foo(int) { std::cout << "Ctor!\n"; } foo(foo const&) { std::cout << "Copy-Ctor!\n"; } ~foo() { std::cout << "Dtor!\n"; } }; int main() { vector<foo> bar(5, 2); }
-
throw; // Passiert auch implizit
nur wenn du noch ein {}-Paar entfernst.
-
camper schrieb:
throw; // Passiert auch implizit
nur wenn du noch ein {}-Paar entfernst.
Ja, fällt mir auch gerade auf. Korrigiert.
-
Vielen Dank, Arcoth, der konkrete Code hat mir sehr geholfen, auch weil ich es vermutlich vergessen hätte die Destruktoren aufzurufen wenn ein Konstruktor wirft.
-
Siehe:
uninitialized_fill()
, bzw.uninitialized_fill_n()
in <memory>.
-
MrFrager schrieb:
Vielen Dank, Arcoth, der konkrete Code hat mir sehr geholfen, auch weil ich es vermutlich vergessen hätte die Destruktoren aufzurufen wenn ein Konstruktor wirft.
Den Code aber nicht 1:1 kopieren. Das war lediglich zu Demonstrationszwecken ausgeschrieben - um zu verdeutlichen wie das ganze (intern) aussehen muss.
Im richtigen Code nimmst du natürlich
std::uninitialized_fill_n
, wie von Furble vorgeschlagen, denn das tut exakt das was ich in den Konstruktor geschrieben habe (abgesehen von der Speicherfreigabe, wie unten von camper angemerkt... :D):vector(std::size_t n, const T& fill) : ptr( static_cast<T*>(operator new(n * sizeof(T))) ), sz(n) { try { std::uninitialized_fill_n( ptr, sz, fill ); } catch( ... ) { delete ptr; throw; } }
-
Arcoth schrieb:
Im richtigen Code nimmst du natürlich
std::uninitialized_fill_n
, wie von Furble vorgeschlagen, denn das tut exakt das was ich in den Konstruktor geschrieben habe:vector(std::size_t n, const T& fill) : ptr( static_cast<T*>(operator new(n * sizeof(T))) ), sz(n) { std::uninitialized_fill_n( ptr, sz, fill ); }
Speicherfreigabe vergessen.