Run-Time Check Failure #2





  • @Swordfish Danke dir, ich werde es mir durchlesen! 🙂



  • @Daniel-l06 sagte in Run-Time Check Failure #2:

    Hey, ich habe deinen Standpunkt verstanden. Ich soll die std::vector benutzen.

    Ja! Und sei froh, dass dein Lehrer / Dozent das erlaubt! Wirklich! Es schlagen hier so viele Leute auf, die auf die eine oder andere Art 1000 Probleme haben, nur weil sie auf gar keinen Fall std::vector benutzen dürfen. (Phänomen "inkompetenter Lehrer")

    Korrekte Ressourcenverwaltung ist nicht leicht, wenn du alles richtig machen willst. Unter anderem musst du dich mit der Frage beschäftigen, was bei Exceptions passiert. Bei einem double als Typ treten die zwar nicht auf (außer jetzt dass das new fehlschlagen könnte), aber bei andere Typen ggf. schon. Siehe auch mal: https://en.wikipedia.org/wiki/Exception_safety
    Wenn du das erstmal außer acht lässt und dann die rule of 3 (oder 5) befolgst, funktioniert dein Code zumindest schonmal. Aber wie gesagt, einfacher wäre, einfach std::vector zu nehmen. Seltsamerweise ist die Frage, wir man Arrays in Python oder ArrayLists in Java implementiert, mir noch nicht untergekommen - da wird einfach munter drauf los benutzt. Nur bei C++ scheint es irgendwie (insbesondere von Anfängern) ein Bedürfnis zu geben, die eingebauten Typen selbst neu schreiben zu wollen...



  • @wob ich denke das Bedürfnis kommt daher, dass C++ eine der wenigen Programmiersprachen ist, wo sich Objekte von Klassen tatsächlich auch wie eingebaute Typen anfühlen.

    Aber ehrlich gesagt, finde ich es auch nicht Schlimm, dass ein Anfänger auch in C++ mal mit new und delete rumärgern sollte.

    In Java ist zumindest eine häufige Übungsaufgabe eine verkettete Liste zu bauen und nicht LinkedList zu verwenden.

    Grüße



  • Ich denke, das kommt von der C-Vergangenheit. Es wird oft behauptet, um C++ zu verstehen, muss man C verstehen.
    Auch wenn das irgendwo vollkommen daneben ist, Phyton und Java müssen sich dieser Vergangenheitsfrage nicht stellen.



  • @zeropage weiß nicht.

    #include <cstddef>
    #include <stdexcept>
    #include <string>
    
    template<typename T>
    class vector_t
    {
    public:
    	using value_type       = T;
    	using pointer          = T*;
    	using reference        = T&;
    	using const_reference  = T const&;
    	using size_type        = std::size_t;
    
    private:
    	std::size_t  data_size;
    	std::size_t  data_capa;  // capacity
    	std::byte   *data;
    
    public:
    	vector_t(std::size_t size = 0, value_type const &value = value_type{})
    	: data_size { size },
    	  data_capa { size },
    	  data      { new std::byte[data_capa * sizeof value_type] }
    	{
    		for (size_type i{}; i < data_size; ++i)
    			new (reinterpret_cast<pointer>(data) + i) value_type{ value };
    	}
    
    	vector_t(vector_t<T> const &other)
    	: data_size { other.data_size },
    	  data_capa { other.data_size },
    	  data      { new std::byte[data_capa * sizeof value_type] }
    	{
    		for (size_type i{}; i < data_size; ++i)
    			new (reinterpret_cast<pointer>(data) + i) value_type{ reinterpret_cast<pointer>(other.data)[i] };
    	}
    
    	friend void swap(vector_t<T> &a, vector_t<T> &b) noexcept
    	{
    		using std::swap;
    		swap(a.data_size, b.data_size);
    		swap(a.data_capa, b.data_capa);
    		swap(a.data, b.data);
    	}
    
    	vector_t& operator=(vector_t<T> other) noexcept
    	{
    		swap(*this, other);
    		return *this;
    	}
    
    	virtual ~vector_t()
    	{
    		for (size_type i{}; i < data_size; ++i)
    			reinterpret_cast<pointer>(data)[i].~value_type();
    		delete[] data;
    	}
    
    	std::size_t size() const noexcept { return data_size; }
    	
    	reference operator[](std::size_t index) { return reinterpret_cast<pointer>(data)[index]; }
    	
    	reference at(std::size_t index)
    	{
    		if (data_size <= index)
    			throw std::out_of_range{ "vector_t::at(" + std::to_string(index) + ")" };
    		return reinterpret_cast<pointer>(data)[index];
    	}
    };
    

    schaut nicht aus wie C.



  • @wob sagte in Run-Time Check Failure #2:

    Aber wie gesagt, einfacher wäre, einfach std::vector zu nehmen. Seltsamerweise ist die Frage, wir man Arrays in Python oder ArrayLists in Java implementiert, mir noch nicht untergekommen - da wird einfach munter drauf los benutzt. Nur bei C++ scheint es irgendwie (insbesondere von Anfängern) ein Bedürfnis zu geben, die eingebauten Typen selbst neu schreiben zu wollen...

    (*this)!



  • Ich glaube, das kommt tatsächlich daher, dass das in C oder C++ tatsächlich eine Herausforderung darstellen kann. Gerade wegen der komplizierteren Speicherverwaltung, Exception Safety, Rule of 3/5/0 usw.
    In Java ist es total langweilig, eine LinkedList zu schreiben. Was soll man dabei lernen?


  • Mod

    @Mechanics sagte in Run-Time Check Failure #2:

    Ich glaube, das kommt tatsächlich daher, dass das in C oder C++ tatsächlich eine Herausforderung darstellen kann. Gerade wegen der komplizierteren Speicherverwaltung, Exception Safety, Rule of 3/5/0 usw.
    In Java ist es total langweilig, eine LinkedList zu schreiben. Was soll man dabei lernen?

    Wenn man's strikt idiomatisch macht, ist die LinkedList auch in C++trivial, weil man dann zur Verbindung Smartpointer nimmt, wodurch Speicherverwaltung entfällt und es - wie in Java - letztlich nur darauf ankäme, die Punkte richtig zu verbinden. Aber wenn die Anfänger ihrem Bedürfnis nachgeben, alle Standardcontainer selber zu implementieren, dann machen sie es ja auch gleich noch so wie es K&R anno 1970 gemacht hätten.



  • @SeppJ würdest Du bitte meinen Code oben mal zerreißen?


  • Mod

    @Swordfish sagte in Run-Time Check Failure #2:

    @SeppJ würdest Du bitte meinen Code oben mal zerreißen?

    Nicht mehr heute Abend 🙂 Erinner' mich morgen, falls ich es nicht selber tu!



  • @SeppJ sagte in Run-Time Check Failure #2:

    Nicht mehr heute Abend Erinner' mich morgen, falls ich es nicht selber tu!

    ¡Si Señor!

    nicht ankreiden daß alles was einen dynamischen vector ausmacht (resize, reserve, ...) fehlt ... erstmal grundgerüst. Noob muss noch üben.



  • @Mechanics sagte in Run-Time Check Failure #2:

    Ich glaube, das kommt tatsächlich daher, dass das in C oder C++ tatsächlich eine Herausforderung darstellen kann. Gerade wegen der komplizierteren Speicherverwaltung, Exception Safety, Rule of 3/5/0 usw.
    In Java ist es total langweilig, eine LinkedList zu schreiben. Was soll man dabei lernen?

    Ich musste trotzdem im Studium eine in Java schreiben 😉

    @SeppJ sagte in Run-Time Check Failure #2:

    Wenn man's strikt idiomatisch macht, ist die LinkedList auch in C++trivial, weil man dann zur Verbindung Smartpointer nimmt, wodurch Speicherverwaltung entfällt und es - wie in Java - letztlich nur darauf ankäme, die Punkte richtig zu verbinden.

    Wobei man grade bei Listen und Bäumen da etwas aufpassen muss, wegen rekursivem Destruktor Aufruf, was zu einem stack overflow führen kann.


  • Mod

    @Swordfish sagte in Run-Time Check Failure #2:

    @SeppJ würdest Du bitte meinen Code oben mal zerreißen?

    👍 Sieht schick aus. Stilistisch würde mich interessieren, wie du die const-Accessors implementieren würdest: Copy&Paste? Nutzung der non-const Member? Anders? (Das ist echtes Interesse, denn ich weiß nicht, welcher Stil dafür momentan "in" ist)



  • @SeppJ ich würde (werde) es so machen:

    #include <cstddef>
    #include <stdexcept>
    #include <string>
    
    template<typename T>
    class vector_t
    {
    public:
    	using value_type       = T;
    	using pointer          = T*;
    	using reference        = T&;
    	using const_reference  = T const&;
    	using size_type        = std::size_t;
    
    private:
    	size_type   data_size;
    	size_type   data_capa;  // capacity
    	std::byte  *data;
    
    public:
    	explicit
    	vector_t(std::size_t size = 0, value_type const &value = value_type{})
    	: data_size { size },
    	  data_capa { size },
    	  data      { new std::byte[data_capa * sizeof value_type] }
    	{
    		for (size_type i{}; i < data_size; ++i)
    			new (reinterpret_cast<pointer>(data) + i) value_type{ value };
    	}
    
    	vector_t(vector_t<T> const &other)
    	: data_size { other.data_size },
    	  data_capa { other.data_size },
    	  data      { new std::byte[data_capa * sizeof value_type] }
    	{
    		for (size_type i{}; i < data_size; ++i)
    			new (reinterpret_cast<pointer>(data) + i) value_type{ reinterpret_cast<pointer>(other.data)[i] };
    	}
    
    	friend void swap(vector_t<T> &a, vector_t<T> &b) noexcept
    	{
    		using std::swap;
    		swap(a.data_size, b.data_size);
    		swap(a.data_capa, b.data_capa);
    		swap(a.data, b.data);
    	}
    
    	vector_t& operator=(vector_t<T> other) noexcept
    	{
    		swap(*this, other);
    		return *this;
    	}
    
    	virtual
    	~vector_t()
    	{
    		for (size_type i{}; i < data_size; ++i)
    			reinterpret_cast<pointer>(data)[i].~value_type();
    		delete[] data;
    	}
    
    	size_type size()     const noexcept { return data_size; }
    	size_type capacity() const noexcept { return data_capa; }
    	
    	      reference operator[](size_type index)       noexcept { return reinterpret_cast<pointer>(data)[index]; }
    	const_reference operator[](size_type index) const noexcept { return reinterpret_cast<pointer>(data)[index]; }
    
    private:
    	reference checked_access(size_type index)
    	{
    		if (data_size <= index)
    			throw std::out_of_range{ "vector_t::at(" + std::to_string(index) + ")" };
    		return reinterpret_cast<pointer>(data)[index];
    	}
    
    public:
    	      reference at(size_type index)       { return checked_access(index); }
    	const_reference at(size_type index) const { return const_cast<vector_t*>(this)->checked_access(index); }
    };
    

    operator[]() copy 'n paste weil ... es ist nur ein einzeiler und wirklich bis auf zweimal const dasselbe.
    at() über eine Hilfsfunktion weil genug Zeilen um bei copy 'n paste messy zu werden.
    Hat jemand andere Ideen?



  • Der

    @Swordfish sagte in Run-Time Check Failure #2:

    vector_t(std::size_t size = 0, value_type const &value = value_type{})
    

    kann als conversion constructor missbraucht werden oder? Sollte der explicit sein?



  • @Swordfish sagte in Run-Time Check Failure #2:

    ...
    kann als conversion constructor missbraucht werden oder? Sollte der explicit sein?

    Ganz entschieden: ja! 🙂



  • @hustbaer ok, geändert. 👍🏻 Hast Du sonst noch etwas?



  • @Swordfish

    Hab grad nicht viel Zeit, was mir schnell aufgefallen ist:

    • Bin mir nicht sicher ob new std::byte[data_capa * sizeof value_type] für Alignment ausreichend ist. Ich glaub schon aber müsste ich nachgucken.
    • reinterpret_cast<pointer>(data) + i beim placement-new ist technisch gesehen falsch: du darfst keinen Zeiger machen auf etwas was es (noch) nicht gibt. D.h. du solltest beim placement new eher data + (i * sizeof T) machen. Der Parametertyp ist ja void* wenn ich mich richtig erinnere.
    • Es kann sein dass du bei den anderen Stellen wo du reinterpret_cast<pointer>(data) + i machst noch std::launder brauchst. Vielleicht kann @Columbo uns sagen ob es nötig ist oder nicht 🙂
      Ansonsten liesse sich das vermeiden indem du das Member zu einem T* machst. Wenn erstmal alles fertig gekonstruiert ist, also nach der placement-new Schleife, ist es ja ein Zeiger auf hintereinanderliegende T*. Müsste also mMn. OK sein.
    • Exception-safe machen oder asserten (static_assert(std::is_nothrow_copy_constructible...)

    Die ganzen Wiederholungen von reinterpret_cast<pointer>(data) + i finde ich auch nicht schön. Bloss ist da wieder die const Sache doof, man bräuchte dann gleich wieder zwei Hilfsfunktionen 😞
    (Fiele aber auch alles weg wenn du nen T* Member machst.)

    Ansonsten finde ich die Benamsung und den Stil stellenweise komisch, aber das is dann wieder ein anderes Thema 🙂



  • @hustbaer sagte in Run-Time Check Failure #2:

    Ansonsten finde ich die Benamsung und den Stil stellenweise komisch, aber das is dann wieder ein anderes Thema

    mach ruhig weiter ... gibs mir ^^


Anmelden zum Antworten