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?


  • Mod

    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.


  • Mod

    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);
    }
    

  • Mod

    throw; // Passiert auch implizit
    

    nur wenn du noch ein {}-Paar entfernst.


  • Mod

    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>.


  • Mod

    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;
            }
        }
    

  • Mod

    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.


Log in to reply