polsmorphe klasse mit operator=()



  • Erzähl doch mal ein paar Worte über die Notwendigkeit dieser Kopieraktionen.



  • Jordy schrieb:

    Naja, eine Clone-Methode würde letzlich auch nichts anderes machen, als per dynamic_cast<>() den typ festellen und über einen Kopier-konstruktor eine neues Objekt erzeugen, oder?

    nicht wirklich:

    class Base {
    public:
      virtual Base* clone() = 0;
    };
    
    class Derived : public Base {
    public:
      virtual Derived* clone() {
        return new Derived(*this);
      }
    };
    

    wobei du natuerlich dir irgendetwas fuer das memory management ueberlegen musst...



  • Das Clone() wäre eine recht gute lösung, für das Zuweisen/Kopieren.

    Dumm wird es, wenn ich auch noch per == vergleichen will (ebenfalls tief). Das scheint mir nur mit widerlichem casten möglich zu sein, oder nicht?



  • In der Überschrift 2.0 ist ein Rechtschreibfehler 2.0: polymorphe



  • Jordy schrieb:

    Dumm wird es, wenn ich auch noch per == vergleichen will (ebenfalls tief). Das scheint mir nur mit widerlichem casten möglich zu sein, oder nicht?

    Wie willst du sinnvoll ein Auto mit einem Boot vergleichen?

    Oder was viel interessanter ist:

    ein Auto mit einem CountDoorOpens_Auto - wo die Anzahl der tueroeffnungen mitgezaehlt wird, vergleichen. Sind die beiden jetzt identisch? oder sind sie unterschiedlich.

    Es klappt nicht gut. Du hast weit mehr probleme als ein paar casts...



  • Shade Of Mine schrieb:

    Jordy schrieb:

    Dumm wird es, wenn ich auch noch per == vergleichen will (ebenfalls tief). Das scheint mir nur mit widerlichem casten möglich zu sein, oder nicht?

    Wie willst du sinnvoll ein Auto mit einem Boot vergleichen?

    Oder was viel interessanter ist:

    ein Auto mit einem CountDoorOpens_Auto - wo die Anzahl der tueroeffnungen mitgezaehlt wird, vergleichen. Sind die beiden jetzt identisch? oder sind sie unterschiedlich.

    Es klappt nicht gut. Du hast weit mehr probleme als ein paar casts...

    Ich würde wollen, daß ich bei verschiedenen Objekten immer false heraus kommt. Wie vergleicht man einen Container?: Anzahl objekte, sonstige member des Containers, Inhalt der Objekte und bei polymorphen objekten eben auch deren typ.

    Und nichts anderes will ich tun. Ich will einen Container für polymorphe objekte entwerfen, den man per = kopieren kann und den man mit == vergleichen kann. Beides tief. Um das tun zu können, müssen die polymorphen objekte eben virtuell zuweisbar sein und virtuell vergleichbar sein. Beides scheint nicht möglich zu sein.

    Immerhin bin ich mit dem Clone() etwas weiter gekommen. So kann ich einen Container elegant clonen.
    Aber von obigen Ziel bin ich trotzdem noch weit entfernt:

    1. Virtuells Vergleichen ist nicht möglich.
    2. Den Container kann ich auch nur so lange mit = zuweisen, solange er selbst nicht auch abgeleitet wird, was aber möglich sein soll, denn ich hätte gerne einen Container, der abgeleitete Objekte seiner selbst verwalten kann.


  • wenn du von objekten den gleichen typ verlangst damit sie gleich sind (was wie schon gesagt ob ein problemdarstellt, zB wenn du eine Klasse nur um funktionalitaet, nicht aber um state erweiterst).

    ich denke ganz ehrlich, dass dein design irgendwie kaputt ist

    mittels typeid() kannst du testen ob der typ von 2 referenzen gleich ist. Damit kannst du dann ein compare mit downcast machen...

    aber du wirst damit viele probleme bekommen.



  • Shade Of Mine schrieb:

    wenn du von objekten den gleichen typ verlangst damit sie gleich sind (was wie schon gesagt ob ein problemdarstellt, zB wenn du eine Klasse nur um funktionalitaet, nicht aber um state erweiterst).

    ich denke ganz ehrlich, dass dein design irgendwie kaputt ist

    mittels typeid() kannst du testen ob der typ von 2 referenzen gleich ist. Damit kannst du dann ein compare mit downcast machen...

    aber du wirst damit viele probleme bekommen.

    Das hast du recht. Aber kaputtes Design? Immerhin habe ich eine Forderung an mich gestellt, die ich absolut nicht falsch finde: Ich will einen Container für polymorphe objekte entwerfen, den man per "=" kopieren kann und den man mit "==" vergleichen kann.

    Wo liegt in dieser Forderung der Fehler?

    Das Kopieren ist schon gut, mit dem Clone(). Mir ist aufgefallen, daß ich, durch die Verwendung von Clone(), nun auch Container im Container verwalten kann, ohne daß ich Probleme bekomme... immerhin kopiere ich die Elemente des Containers nicht durch Zuweisung sondern durch Clonen... und das ist ja virtuell. Damit sind tiefe kopien beliebig verschachtelter Container- und Objekt-Hierarchien möglich, auch von polymorphen. Und diese neue Eigenschaft ist schon sehr wertvoll.

    Die Clone Funktion unterstützt mich auch beim Zuweisen von einem Container an einen anderen, denn dadurch ist es möglich, bei "a=b;" die Elemente von a zu löschen und durch Kopien der Elemente von b zu ersetzen.

    Der ursprüngliche Gedanke, virtuell zwei beliebige polymorphe Container einander zuzuweisen, ist ziemlich bescheuert. Schließlich ist das ja garnicht sinnvoll möglich, wie schon bemerkt wurde. Um einen polymorphen Container einem anderen zuweisen zu können, müssen sie den selben Typ haben, darum kann ich auch mit gutem Gewissen einen neuen operator=() für jeden abgeleiteten Container-Typ schreiben. Leider habe ich diesen Gedanken mit dem Problem des virtuellen Clones durcheinander gebracht, das durchaus einfach lösbar war (danke!). Der Fähigkeit eines Containers, die Elemente eines anderen Containers zu kopieren, beeinträchtigt das nicht, da ich dafür ausschließlich Clone verwende.

    Allerdings: Wenn jemand auf die Idee kommt, zwei abgeleitete Container über einen basiszeiger einander zuzuweisen, dann geht das schief. Ebenso wäre es ein unglück, wenn jemand richtig zuweist (also ohne basiszeiger), aber vergißt, einen operator=() für seine abgleiteten Container zu schreiben. In beiden Fällen würde dann der operator=() des Basis-Containers verwendet, was aber nicht ausreicht, wenn der abgleitete Container eigene Member hat.
    Schade nur, daß mir keine vernünftige methode einfällt, um sicherzustellen, daß diese beiden Fehler verhindert werden. Aber damit, daß polymorphe Vererbung etwas kitzlig ist, muß man wohl leben 😕

    Letztlich bleibt das Vergleichen von Containern: Dafür habe ich genau auch nur die Lösung gefunden, die dir auch eingefallen ist: typeid() vergleichen. So kann ich ich zwei Container miteinander vergleichen, ohne Rücksicht auf die polymorphie der enthaltenen Elemente nehmen zu müssen. Leider würde ich hier nicht um den downcast herum kommen, in den operator==() Methoden der abgeleiteten Elemente. Denn wenn die Typen gleich sind, soll schließlich noch ein tiefer Vergleich durchgeführt werden. Was ich hiervon halten soll, weiß ich nicht. Aber letztendlich kann man damit wohl schon leben. dynamic_cast ist ja halbwegs sicher, wenn man sich exceptions werfen läßt, falls irgendjemand es doch schafft, zwei verschiedene typen polymorpher objekte zu vergleichen.

    Wenn du eine bessere Idee hast, wie man polymorphe Objekthierarchien vergleichen kann, sags mir bitte!



  • Was mich noch interessieren würde... wie lösen andere Programmiersprachen das Problem?

    Poylmorphe Hierarchien sind doch eigentlich alltäglich. Und trotzdem ist so eine einfach Sache, wie der Vergleich von einer Hierarchie mit einer anderen schon eine große Herausforderung.

    Berücksichtigen andere OO Sprachen sowas?

    Bin ich der erste, der solche probleme hat?!



  • Ich bin eigentlich eher die Sichtweise gewohnt, das ich lauter Basisobjekte habe. Es wird nur mit den Basisobjekten gearbeitet. Nur mit dem, was das Basisobjekt bietet.

    Da alle Basisobjekte gleich sind, ist mir nie das Bedürfnis gekommen diese auch zu vergleichen.

    In einem Objekt etwas anderes zu sehen als das Basisobjekt unterläuft irgendwie den Sinn der Sache.

    Basisobjekt == Basisobjekt, alles andere lässt man virtuelle Methoden erledigen. 😕



  • Knuddlbaer schrieb:

    Ich bin eigentlich eher die Sichtweise gewohnt, das ich lauter Basisobjekte habe. Es wird nur mit den Basisobjekten gearbeitet. Nur mit dem, was das Basisobjekt bietet.

    Da alle Basisobjekte gleich sind, ist mir nie das Bedürfnis gekommen diese auch zu vergleichen.

    In einem Objekt etwas anderes zu sehen als das Basisobjekt unterläuft irgendwie den Sinn der Sache.

    Basisobjekt == Basisobjekt, alles andere lässt man virtuelle Methoden erledigen. 😕

    Eine fahrzeugklasse. Davon abgleitet Auto, Schiff, Flugzeug uva. Die Fahrzeuge sind polymorph und haben sehr viele unterschiedliche Eigenschaften.

    Du hast zwei Listen, mit Fahrzeugen. Wild gemischt Autos, Schiffe usw.

    Ich möchte wissen: Sind die beiden Listen identisch (enthalten sie also die gleichem Fahrzeuge und das in gleicher Reihenfolge?). Dafür brauche ich eine Funktion. Es soll nur true kommen, wenn beide Listen und ihre Fahrzeuge völlig identisch sind.

    Wie programmierst du das?



  • Erst mal garnicht.

    An dem Punkt in dem ich Schiffe Autos Panzer etc. als Fahrzeuge in eine
    liste Packe, ist mir der Typ des Objektes egal. Ich hab alles auf Fahrzeuge
    reduziert.

    Wenn ich die Listen gegeneinander prüfen muss, ob Sie die >>identischen<<
    Objekte haben, kann ich dies über das Basisobjekt machen.

    Wenn es darum geht ob die >>gleichen<< Fahrzeuge in gleicher Reihenfolge
    in zwei unterschiedlichen Listen vorhanden sind, sollte man sich Gedanken
    machen warum das so ist, denn es passt nicht in die Polymorphy rein.

    Hierzu fällt mir "nur" ein, jedem Objekt eine virtuelle compare Methode zu
    verpassen. Wenn nun aber jemand erbt ohne diese Methode neu zu definieren,
    wird später eventuell ein LKW für ein Auto gehalten, weil LKW diese Methode
    nicht implementiert hat.

    Ebenso fehlt mir auf anhieb die Idee, den Typ des zweiten Objektes zu bestimmen.
    Alles was mir einfällt wird zu Murks und käme der Idee kaputtes Design sehr nahe.
    Lediglich eine virtuelle Methode, die den Typ zurück gibt fänd ich einleuchtend.
    Jedoch ergibt sich das Problem, das diese Implementiert sein muss.

    IMHO kommt man über die typeID und dem Basiszeiger nicht heran:

    #include <iostream>
    #include <string>
    
    struct A
    {
    	virtual std::string name(){return "A";}
    };
    
    struct B : public  A
    {
    	virtual std::string name(){return "B";}
    };
    struct C : public  B
    {
    	virtual std::string name(){return "C";}
    };
    
    struct D: public  A
    {
    	virtual std::string name(){return "D";}
    };
    
    struct E
    {
    	virtual std::string name(){return "E";}
    };
    struct CE: public E,public C
    {
    	virtual std::string name(){return "CE";}
    };
    
    int main(int argc, char* argv[])
    {
    	CE ce;
    	C  c;
    
    	CE * ptr_ce = &ce;
    
    	A * ptr = ptr_ce;
    	B * ptr2 = ptr_ce;
    	C * ptr3 = ptr_ce;
    	E * ptr4 = ptr_ce;
    	B * ptr5 = &c;
    
    	std::cout<< typeid(ptr_ce).name() << " ist ein " << ptr_ce->name() <<std::endl;
    	std::cout<< typeid(ptr).name()  << " ist ein " << ptr->name()<<std::endl;
    	std::cout<< typeid(ptr2).name() << " ist ein " << ptr2->name()<<std::endl;
    	std::cout<< typeid(ptr3).name() << " ist ein " << ptr3->name()<<std::endl;
    	std::cout<< typeid(ptr4).name() << " ist ein " << ptr4->name()<<std::endl;
    	std::cout<< typeid(ptr5).name() << " ist ein " << ptr5->name()<<std::endl;
    
    	return 0;
    }
    
    // Jup, ich weiß da fehlt nen virt ~ etc.
    

    struct CE * ist ein CE
    struct A * ist ein CE
    struct B * ist ein CE
    struct C * ist ein CE
    struct E * ist ein CE
    struct B * ist ein C

    Mit meinem Wissensstand wird es notwendig jedem Objekt eine Methode zu verpassen,
    die Auskunft über den Typ gibt. Je nachdem was mit dem Vergleich der Listen erreicht
    werden muss, lässt sich der Vergleich aber ganz umgehen.

    So allgemein gehalten fühlt sich die Notwendigkeit einen solchen vergleich machen
    zu müssen falsch und riskant an, sobald eine klasse die erbt die implementierung hierfür
    nicht bereit stellt , wird es ein falsches Ergebnis liefern. Was soll passieren, wenn
    Mehrfachvererbung ins Spiel kommt ? Was wenn ich ein schwimmendes Auto haben will und so
    vererbe ?

    struct Fahrzeug
    {
    	virtual void fahren() = 0;
    };
    
    struct Auto : public Fahrzeug
    {
    	virtual void fahren(){ std::cout<<"brumm"<<std::endl;}
    };
    
    struct Schiff : public Fahrzeug
    {
    	virtual void fahren(){ std::cout<<"blubber"<<std::endl;}
    };
    
    struct schwimmendesAuto : public Auto, public Schiff
    {
    	virtual void fahren(){ Schiff::fahren(); Auto::fahren();}
    };
    
    	Fahrzeug * f =  reinterpret_cast<Fahrzeug*>(new schwimmendesAuto);
    
    	f->fahren();
    

    Eventuell fehlt mir aber auch KnowHow oder ich habe den Beitrag nicht genau genug gelesen.



  • Knuddlbaer schrieb:

    Wenn es darum geht ob die >>gleichen<< Fahrzeuge in gleicher Reihenfolge
    in zwei unterschiedlichen Listen vorhanden sind, sollte man sich Gedanken
    machen warum das so ist, denn es passt nicht in die Polymorphy rein.

    Ja, es geht um Gleicheit, nicht um Identität.

    Und warum passt ein Vergleich zweier Listen nicht in die Polymorphie?

    Wie soll man eine Reihe von dynamischen Objekten, die in einer Liste verwaltete werden, sonst vergleichen?!

    Ob die Objekte polymorph sind, oder nicht ist doch erstmal egal. Die Polymorphie sollte mir allenfalls *helfen*, ohne ständige Typunterscheidungen mit den Objekten in der Liste zu arbeiten. Sie sollte mir helfen dieses Ziel zu erreichen, *behindern* kann sie es doch kaum!?

    Knuddlbaer schrieb:

    Hierzu fällt mir "nur" ein, jedem Objekt eine virtuelle compare Methode zu
    verpassen. Wenn nun aber jemand erbt ohne diese Methode neu zu definieren,
    wird später eventuell ein LKW für ein Auto gehalten, weil LKW diese Methode
    nicht implementiert hat.

    Und genau das ist der Punkt. Das wäre ja genau die Lösung.

    Polymorphie entsteht durch virtuelle Funktionen. Ich will also eine virtuelle Compare-methode schreiben (oder eben einen virtuellen operator==()). Die Frage ist aber: Wie? Das problem ist, daß die virtuellen Methoden, die mir einfallen würden, immer ein dynamic_cast benötigen.

    Alle sagen mir ich mache einen Design-Fehler. Aber keiner kann mir wirklich erklären *welchen*.

    Das beispiel mit den Fahrzeugen in zwei Listen ist doch logisch nachvollziehbar. Solange mir keiner sagt, warum der Wunsch ilegitim sein, zwei solcher Listen zu vergleichen, suche ich nach einer implementation.
    Aber dafür hat auch niemand eine Lösung. Soll ich nicht vererben? Soll ich keine Liste mit Basiszeigern verwenden? Soll ich keine Polymorphie verwenden? oder soll ich schlicht dynamic_cast benutzen, ohne schlechtes Gewissen?



  • Na, nen Krümel hin werfen und verlangen das jemand den ganzen Vorgang fürs Brotbacken erklärt funktioniert nicht.

    Erkläre doch mal >>Warum<< Du diesen Abgleich der Listen benötigst.
    Solange Du nicht sagen kannst warum Du den abgleich der Listen brauchst, ist er unnötig. Die Antwort ist wenig Hilfreich aber genauso schön allgemein.

    AFAIK gibt es keine Möglichkeit aus einem Basiszeiger die Information herauszubekommen was er denn mal war.

    Ich habe bisher nie einen tiefen vergleich benötigt. Ich habe stehts einen Weg über eine virtuelle Methode gewählt.

    Wie würde der vergleich denn mit dynamic_cast aussehen ? Vllt. hilft mir das den Horizont zu erweitern und ne Idee zu liefern.



  • Wenn du es wirklich willst, und wie gesagt, ich halte es fuer eher kaputt, kannst du natuerlich die standard variante mit dynamic_cast machen (was anderes geht nicht):

    virtual void compare(Base& o) {
      Derived& other=dynamic_cast<Derived&>(other);
      return this.foo == other.foo;
    }
    

    aber an Bloch denken, der in effective Java schreibt:

    There is simply no way to
    extend an instantiable class and add an aspect while preserving the equals
    contract.

    und equals bezieht sich hier auf unsere compare methode.

    solche vergleiche mit nicht-value klassen sind oft ein problem. es macht idR auch keinen sinn solche vergleiche zu verlangen...



  • Ok, auch wenn ich nix zu beitragen konnte, hat mir der Beitrag was gebracht 🤡

    thx @ Shade



  • Knuddlbaer schrieb:

    Da alle Basisobjekte gleich sind, ist mir nie das Bedürfnis gekommen diese auch zu vergleichen.

    In einem Objekt etwas anderes zu sehen als das Basisobjekt unterläuft irgendwie den Sinn der Sache.

    Basisobjekt == Basisobjekt, alles andere lässt man virtuelle Methoden erledigen. 😕

    Die Argumentation kann ich nicht nachvollziehen. Wieso sind alle Basisobjekte gleich? Sind dann auch alle ints gleich? int==int und ints vergleichen macht keinen Sinn?



  • Weil ein Basisobjekt ein Basisobjekt ist ?

    Also ein Fahrzeug ein Fahrzeug und kein Schiff Panzer oder sonst was ?!

    Ich kann ein Basisobjekt mit einem Basisobjekt vergleichen wie ein int mit einem int. Ich kann aber kein std::string mit einem int vergleichen wie ich kein schiff mit einem panzer vergleichen kann.



  • Vielleicht würden sich alle leichter tun wenn du statt Autos und Booten mal konkret schreibst um was es geht (oder hab ich das überlesen?).
    Ansonsten sehe ich das Problem nicht. Die Lösung heisst typeid. Guckst du:

    #include <cassert>
    #include <string>
    #include <memory>
    #include <iostream>
    
    class Vehicle
    {
    public:
    	Vehicle(int maxSpeed)
    		:	m_maxSpeed(maxSpeed)
    	{
    	}
    
    	bool operator == (Vehicle const& other) const // final
    	{
    		// virtual call to CompareImpl of whatever class (*this) is
    		return CompareImpl(other);
    	}
    
    	bool operator != (Vehicle const& other) const // final
    	{
    		// virtual call to CompareImpl of whatever class (*this) is
    		return !CompareImpl(other);
    	}
    
    	// virtual interface:
    	virtual std::string Foo()
    	{
    		return "Vehicle";
    	}
    
    protected:
    	// compare the Vehicle part of 2 Vehicles
    	bool CompareVehicles(Vehicle const& other) const
    	{
    		return m_maxSpeed == other.m_maxSpeed;
    	}
    
    private:
    	// only ever called by operators == and != of Vehicle
    	// override in every derived class
    	virtual bool CompareImpl(Vehicle const& other) const
    	{
    		assert(typeid(*this) == typeid(Vehicle)); // (*this) MUST be a Vehicle (and NO derived class)
    		if(typeid(other) == typeid(Vehicle))
    			return CompareVehicles(other);
    		else
    			return false;
    	}
    
    	int m_maxSpeed;
    };
    
    class Car
    	:	public Vehicle
    {
    public:
    	Car(int maxSpeed, int wheelCount)
    		:	Vehicle(maxSpeed),
    			m_wheelCount(wheelCount)
    	{
    	}
    
    	// virtual interface:
    	virtual std::string Foo()
    	{
    		return "Car";
    	}
    
    protected:
    	// compare the Car part of 2 Cars
    	bool CompareCars(Car const& other) const
    	{
    		return CompareVehicles(other) && (m_wheelCount == other.m_wheelCount);
    	}
    
    private:
    	// see Vehicle::CompareImpl
    	virtual bool CompareImpl(Vehicle const& other) const
    	{
    		assert(typeid(*this) == typeid(Car)); // (*this) MUST be a Car (and NO derived class)
    		if(typeid(other) == typeid(Car))
    			// since we now know the exact type of (other) we don't need a slow dynamic_cast
    			return CompareCars(static_cast<Car const&>(other));
    		else
    			return false;
    	}
    
    	int m_wheelCount;
    };
    
    class Boat
    	:	public Vehicle
    {
    public:
    	Boat(int maxSpeed, int draught)
    		:	Vehicle(maxSpeed),
    			m_draught(draught)
    	{
    	}
    
    	// virtual interface:
    	virtual std::string Foo()
    	{
    		return "Boat";
    	}
    
    protected:
    	bool compareBoats(Boat const& other) const
    	{
    		return CompareVehicles(other) && (m_draught == other.m_draught);
    	}
    
    private:
    	// see Vehicle::CompareImpl
    	virtual bool CompareImpl(Vehicle const& other) const
    	{
    		assert(typeid(*this) == typeid(Boat)); // (*this) MUST be a Boat (and NO derived class)
    		if(typeid(other) == typeid(Boat))
    			// since we now know the exact type of (other) we don't need a slow dynamic_cast
    			return compareBoats(static_cast<Boat const&>(other));
    		else
    			return false;
    	}
    
    	int m_draught;
    };
    
    int main(int argc, char** argv)
    {
    	std::auto_ptr<Vehicle> vehicle(new Vehicle(10));
    	std::auto_ptr<Vehicle> car1(new Car(10, 4));
    	std::auto_ptr<Vehicle> car2(new Car(10, 4));
    	std::auto_ptr<Vehicle> car3(new Car(10, 6));
    	std::auto_ptr<Vehicle> boat1(new Boat(10, 4));
    	std::auto_ptr<Vehicle> boat2(new Boat(10, 4));
    	std::auto_ptr<Vehicle> boat3(new Boat(10, 99));
    
    	assert(*vehicle == *vehicle);
    	assert(*car1 != *vehicle);
    	assert(*boat1 != *vehicle);
    	assert(*car1 == *car2);
    	assert(*car1 != *car3);
    	assert(*car2 != *car3);
    	assert(*boat1 == *boat2);
    	assert(*boat1 != *boat3);
    	assert(*boat2 != *boat3);
    
    	assert(*car1 != *boat1);
    	assert(*car2 != *boat1);
    	//...
    
    	std::cout << "Vehicle: " << vehicle->Foo() << std::endl;
    	std::cout << "Car: " << car1->Foo() << std::endl;
    	std::cout << "Boat: " << boat1->Foo() << std::endl;
    	std::cout << std::flush;
    
    	return 0;
    }
    

    Funktioniert wunderbar. Und typeid(a) == typeid(b) würde ich immer einem dynamic_cast vorziehen, ganz einfach weil es schneller ist, und weil du mit dynamic_cast einen doofen Tanz aufführen musst um draufzukommen ob denn das zu testende Objekt wirklich ein "Bar" ist oder nur ein von "Bar" abgeleitetes etwas. Gehen tut es schon, sieht dann aber so aus (*würg*):

    Vehicle* v = GimmeSomeVehicle();
    	// see if v is a car (and NOT an instance of a derived type)
    	if((dynamic_cast<Car*>(v) != 0) && (dynamic_cast<void*>(v) == static_cast<Car*>(v)))
    		DoSomethingWithCar(static_cast<Car*>(v));
    

    Erklärung: dynamic_cast<void*> gibt die Adresse des "most derived type" zurück. Wenn diese gleich der Adresse von "v auf Car gecastet" ist, dann muss v ein Car sein und kein von Car abgeleiteter Typ. Da wir aber nicht einfach v auf Car* casten können ohne überhaupt zu wissen ob v ein Car oder von Car abgeleiteter Typ ist müssen wir das vorher nochmal prüfen, daher der Test (dynamic_cast<Car*>(v) != 0).

    Wie gesagt alles viel zu kompliziert, von daher lieber typeid verwenden.

    Falls die Objekte um die es geht serialisierung unterstützen ist es überhaupt recht einfach, denn dann musst du bloss die serialisierten Daten vergleichen. In den serialisierten Daten muss ja wohl eine Art eindeutiger Typenkennung vorkommen, sonst kannst du sie ja nie mehr deserialisieren.

    Ahja, das Zuweisen... . Entweder du klonst wirklich die Objekte anstatt Zuweisungn zu verwenden (dann musst du eben damit leben das Alte wegzuwerfen und ein Neues zu machen), oder aber du wirst auf das Problem stossen dass du die Zuweisung "DerivedType = BaseType;" irgendwie behandeln musst. Entweder man verbietet es und du wirfst einen std::logic_error, oder aber du musst den DerivedType mit irgendwelchen Defaultwerten füttern. Auch doof. Lässt sich aber auf jeden Fall gleich wie die Operatoren == und != oben umsetzen.

    Im übrigen gehe ich auch davon aus dass es ein eleganteres Design geben müsste. Kann ich aber nicht sagen solange du nicht sagst worum es konkret geht.



  • Hi,

    nur mal als andere Technik reingeworfen (habe keine Ahnung, ob das für Dich passt): operator==() kann man auch als freie binäre Funktion definieren. Es muß keine Memberfunktion sein.
    Damit könntest Du entsprechende Overloads für jedes Typpaar bereitstellen (vielleicht soll ja ein Vergleich "LKW == Schiff" anders vonstatten gehen als "LKW == Auto" ...)
    Kann natürlich recht aufwendig sein, wenn man viele Typen hat ... und man sollte die "symmetrische Version" nicht vergessen, damit "LKW == Schiff" nichts anderes ergibt als "Schiff == LKW" ... es sei denn man will genau das.
    Und man muß sich natürlich "Generiziztät" ein wenig anders nähern.

    Nur mal so als Idee....
    (gilt natürlich auch für operator=())

    Gruß,

    Simon2.


Anmelden zum Antworten