Vector mit verschiedenen Datentypen?



  • Sowas habe ich vermutet, ich war mir nur nicht sicher, welche Konstruktoren und Operatoren ich noch implementieren sollte...

    boost::any? *google*

    thx btw



  • noNeed 4 aNick schrieb:

    Sowas habe ich vermutet, ich war mir nur nicht sicher, welche Konstruktoren und Operatoren ich noch implementieren sollte...

    Dann schreib dir die "Rule of Three" ganz fett ins Notizbuch: "Wer einen der 'großen Drei' (Kopier-Konstruktor, Zuweisungsoperator, Destruktor) benötigt, braucht in der Regel alle drei." (es gibt einige Ausnahmen von dieser Regel, aber nicht sehr viele)

    boost::any? *google*

    Du verwendest doch schon Boost, da mußt du dich nur etwas genauer umsehen (Boost::any ist afaik eine C++ Alternative zu unions).



  • Dein Java-Beispiel funktioniert deshalb nicht, weil es in Java keine Zeiger gibt. Die "Objekte", die du in Java in dem vector speicherst, sind sowas wie Zeiger in C++. Und genauso kannst du das in C++ auch machen: Schreibe eine Oberklasse, sowei abgeleitete Klassen, die deine Daten kapseln. Dann speicherst du in deinem Vector Zeiger auf diese Oberklasse. An die eigentlichen Daten kommst du dann durch Polymorphie und evtl. explizites Downcasting ran.



  • Alles klar.

    Ich wollt auch nur kurz die Boost-Docs überfliegen, die liegen zwar auch lokal irgendwo, aber mit google bin ich halt schneller 😉



  • Das sieht alles sehr unschön aus, wegen der ganzen Casterei, und das ist dann auch wegen dem void* nicht mehr typensicher!
    Hier würde sich der Einsatz von Templates anbieten.

    #include <cassert>
    #include <vector>
    #include <string>
    
    class ValueImplBase
    {
    public:
    	virtual ~ValueImplBase() { }
    	virtual ValueImplBase* clone() const = 0;
    };
    
    template<typename T>
    class ValueImpl : public ValueImplBase
    {
    public:
    	ValueImpl(const T& object)
    		: object_(object)
    	{
    	}
    
    	ValueImplBase* clone() const
    	{
    		return new ValueImpl<T>(object_);
    	}
    
    	T& value()
    	{
    		return object_;
    	}
    
    private:
    	T object_;
    };
    
    class Value
    {
    public:
    	template<typename T>
    	Value(const T& object)
    		: p_impl_(new ValueImpl<T>(object))
    	{
    	}
    
    	Value(const Value& value)
    		: p_impl_(0)
    	{
    		*this = value;
    	}
    
    	~Value()
    	{
    		delete p_impl_;
    	}
    
    	template<typename T>
    	T& value()
    	{
    		ValueImpl<T>* p_casted = dynamic_cast<ValueImpl<T>*>(p_impl_);
    		assert(p_casted);
    		return p_casted->value();
    	}
    
    	Value& operator =(const Value& rhs)
    	{
    		delete p_impl_;
    		p_impl_ = rhs.p_impl_ ? rhs.p_impl_->clone() : 0;
    		return *this;
    	}
    
    private:
    	ValueImplBase* p_impl_;
    };
    
    void test()
    {
    	std::vector<Value> myVector;
    
    	myVector.push_back(Value(42));
    	myVector.push_back(Value(std::string("Ich bin ein String!")));
    	myVector.push_back(Value(42.0));
    
    	Value& v0 = myVector[0];
    	v0.value<int>() += 2;
    
    	Value& v1 = myVector[1];
    	v1.value<std::string>() += " Jetzt wird mir auch noch was angehängt!";
    
    	Value& v2 = myVector[2];
    	v2.value<double>() /= 2.0;
    
    	// !!! Das führt zu einem Fehler, da v0 int enthält und nicht short !!!
    	v0.value<short>()++;
    }
    


  • pumuckl schrieb:

    Dein Java-Beispiel funktioniert deshalb nicht, weil es in Java keine Zeiger gibt. Die "Objekte", die du in Java in dem vector speicherst, sind sowas wie Zeiger in C++. Und genauso kannst du das in C++ auch machen: Schreibe eine Oberklasse, sowei abgeleitete Klassen, die deine Daten kapseln. Dann speicherst du in deinem Vector Zeiger auf diese Oberklasse. An die eigentlichen Daten kommst du dann durch Polymorphie und evtl. explizites Downcasting ran.

    Ja, das hatte ich auch schon überlegt. Schließlich löst Java das intern mehr oder weniger auch so, dass es nur Referenzen auf Objekte hält...

    Hab nun eben noch den CpyCtr und den =Op geschrieben, nun klappts wunderbar.
    Wenn ich das nächste mal so eine Konstruktion benötige, werd ich aber wohl einfach boost::any benutzen - es reicht, wenn ich einmal das Rad neuerfunden hab 😉



  • Du kannst auch meine "tolle" Lösung nehmen, die ich vorhin extra für dich geschrieben habe, weil mir so langweilig ist ... 😞



  • Warte, hier haste nen Keks 😉

    Zugegeben, die ist auch hübsch geworden... Wie isn das mit der Typensicherheit bei mir? Ist das nicht durch die entsprechenden Abfragen sowieso gegeben?

    Naja, ich mach jetzt erstmal mittach 🙂



  • Ja, bei dir wird so nix schiefgehen, aber wenn du irgendwo nach void* castest, gehen die Typinformationen verloren. So könntest du einen Apfel nach void* casten und dann das void* nach Birne casten, und du hättest aus dem Apfel eine Birne gemacht - wohlgemerkt eine korrupte, kaputte (verfaulte) Birne, die du direkt wegschmeißen kannst. 🤡



  • Unter typsicherheit wird normalerweise auch die im COmpiler eingebaute Typsicherheit verstanden und keine selbstgebaute. Natuerlich kann man sich einfach eine handvoll Byte leeren Speicher reservieren und sich auf irgendeine Art merken, dass das was drinsteht als int zu behandeln ist - sich einen int zu reservieren ist aber trotzdem sicherer und einfacher 😉


Anmelden zum Antworten