polsmorphe klasse mit operator=()
-
Die Frage ist doch, was soll das dann bedeuten?
Fahrzeug * f = new Schiff; Auto * a = new Auto; *f=*a; // soll Fahrzeug jetzt plötzlich ein Auto sein? *a=*f; // wie kann aus einem Fahrzeug ein Auto werden, das geht doch garnicht immer. Wenn beispielsweise Fahrzeug in Wirklichkeit ein Schiff ist.Nach meiner Erfahrung sollte man für polymorphe Typen lieber auf die meisten Operatorenüberladungen verzichten. Man muß letztlich eh mit Zeigern oder Referenzen arbeiten, um korrekt mit den Teilen umgehen zu können.
-
Jester schrieb:
Die Frage ist doch, was soll das dann bedeuten?
Fahrzeug * f = new Schiff; Auto * a = new Auto; *f=*a; // soll Fahrzeug jetzt plötzlich ein Auto sein? *a=*f; // wie kann aus einem Fahrzeug ein Auto werden, das geht doch garnicht immer. Wenn beispielsweise Fahrzeug in Wirklichkeit ein Schiff ist.Nach meiner Erfahrung sollte man für polymorphe Typen lieber auf die meisten Operatorenüberladungen verzichten. Man muß letztlich eh mit Zeigern oder Referenzen arbeiten, um korrekt mit den Teilen umgehen zu können.
Fahrzeug * f1 = new Schiff; Fahrzeug * f2 = new Schiff; Fahrzeug * f3 = new Auto; *f1=*f2; // Hier soll das ganze schiff kopiert werden, nicht nur ein Teil! *f2=*f3; // Hier nur die gemeinsame basis (wenn sowas jemand macht)Ich will einfach verhindern, daß jemand, der die Objekte (sie sind ja polymorph, das heißt sie werden überlicherweise über basiszeiger gehandhabt) über basiszeiger einander zuweist, den linken operanden in einen illegalen zustand versetzt. Wenn er zwei objekte *gleichen* typs zuweist, soll auch *alles* kopiert werden, nicht nur die basis.
Oder anders forumliert: Ich will, daß polymorphe Klasse, sich immer polymorph verhalten, auch bei operator-aufrufen.
Das heißt wohl, wenn ich polymorphe klassen schreibe, die ich einander zuweisen möchte, muss ich letztendlich über zeiger und dynamic_cast<>() gehen? Igitt.
Oder umgekehrt: Polymorphe Objekte dürfen niemals einen operator=() besitzen, weil man sonst jede Menge Unsinn machen kann? Das wäre ja noch schlimmer.
Mir ist nie aufgefallen, daß polymorphe Objekte so untauglich für handle-by-value sind. Mir scheint das wäre nur sauber lösbar, wenn für die parameter virtueller methoden (und damit für operanden) polymorphie erlaubt wäre (so wie es für den *Rückgabewert* virtueller methoden ja inzwischen erlaubt ist).
Gibt es für sowas kein Lösungsmuster (ohne dynamic_cast)?!
-
groovemaster schrieb:
Du könntest es mit einer virtuellen Funktion probieren, die die eigentliche Zuweisungsarbeit übernimmt.
Wobei das nicht hundertprozentig sicher ist, bei Zuweisungen wie B = A gibt es zB Probleme. Kann man aber sicherlich noch beseitigen.Ohne dynamic_cast? wie?
-
man verwendet keine operatoren ueberladung in polymorphen klassen weil man damit dem sinn der polymorphie entgegen wirkt.
Wenn ich sage:
ein Auto ist ein Fahrzeug
und
ein Fahrrad ist ein Fahrzeugdann macht es keinen Sinn zu sagen Auto A = Fahrrad B
Du muesstest hier also eine Exception fliegen lassen.Aber genau das ist ja eigentlich der Vorteil von Polymorphie gewesen, dass es mir komplett egal sein kann ob dieses Fahrzeug ein Auto oder Fahrrad ist. Wenn ich den Upcast auf Fahrzeug mache, gebe ich das Wissen um den genauen Typen auf und ich will alle Typen gleich behandeln. Weil ja alles Fahrzeuge sind.
Es waere sehr verwirrend wenn ich
Fahrzeug A= Fahrzeug B
machen kann aber
Fahrzeug A = Fahrzeug C
nicht.Wenn du bei polymorphen Klassen kopieren willst, dann verwende eine "clone" methode (wie in Java).
oder gib die Polymorphie auf. Du kannst nicht beides haben und zusaetzlich noch ordentlichen Code. Du kannst zwar mit dynamic_cast und sonstigen rtti sachen dir etwas zusammen hacken - aber sowas sollte immer stark hinterfragt werden ob es wirklich noetig ist.
-
Shade Of Mine schrieb:
Wenn du bei polymorphen Klassen kopieren willst, dann verwende eine "clone" methode (wie in Java).
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?
-
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:- Virtuells Vergleichen ist nicht möglich.
- 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 CMit 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...