Run-Time Check Failure #2



  • @Swordfish Echt? ^^' Also es sieht halt jetzt so aus. Im prinzip müsste ich doch jetzt nur noch eine Funktion einbauen, die den Speicher wieder freigibt und diese immer kurz vor Ende des Programms ausführen, oder?

    #pragma once
    class CMyVektor
    {
    private:
    	int dimension;
    	double* vektor;
    public:
    	int createV(int dim);
    	int getDimension();
    	double setComp(int comp, double compWert);
    	double getComp(int comp);
    	double getLength();
    	int getVektor();
    };
    
    int CMyVektor::createV(int dim)
    {
    	dimension = dim;
    	vektor = new double[dimension];
    
    	return 0;
    }
    


  • @Daniel-l06

    oder?

    Nein.



  • @manni66 Hey, ich habe deinen Standpunkt verstanden. Ich soll die std::vector benutzen. Schön. Wenn du mir aber sonst nicht weiter helfen möchtest, dann lass es doch einfach? Ich möchte es halt ohne versuchen. Wenn ich ohne absolut nicht mehr weiter komme greife ich darauf zurück, mein gott. Lass mich doch probieren und gucken was geht und was nicht.





  • @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! 🙂


Anmelden zum Antworten