polsmorphe klasse mit operator=()
-
Simon2 schrieb:
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" ...)Ne, geht nicht. C++ kann keine multimethods.
Du hantierst ja nur mit Base* rum... und dann nimmt C++ auch nur den Typ Base* zum aufloesen welche Funktion aufgerufen wird.
-
Shade Of Mine schrieb:
...Du hantierst ja nur mit Base* rum...
Stimmt schon, ist ja Laufzeitpolymorphie - hatte ich im ersten Elan gar nicht gesehen. *hand-an-die-stirn-klatsch*
Dabei hilft natürlich die (template-)STL und das Overloading mit ihrer Compilezeitpolymorphie nicht wirklich weiter ....Tja, wenn's Laufzeit sein muß, kann ich auch nicht weiterhelfen.
Schönen Abend noch,
Simon2.
-
Shade Of Mine schrieb:
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; }Hehe.. dachte schon dass sich da ein kleiner std::bad_cast eingeschlichen hat. Aber es geht ja gar nicht um equals geht.
Meine Variante:
virtual int compare( base const& other ) const { throw std::runtime_exception("This method shouldn't exist, let alont be called!"); } virtual bool equals( base const& other ) const { // if you must, why the heck not? if (derived const* d = dynamic_cast<derived const*>(&other)) { return some_attribute() == other->some_attribute(); } else { return false; } }
-
finix schrieb:
Hehe.. dachte schon dass sich da ein kleiner std::bad_cast eingeschlichen hat.
der ist absicht.
damits "bumm" geht, wenn jemand mist baut.
aber das ist halt je nachdem wie man es lieber hat...
-
Ist mir dann auch klar geworden. Dachte ja auch erst es geht um equals.
-
Knuddlbaer schrieb:
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.
So? Ich kann ein Schiff mit einem Panzer vergleichen. Und solange ich zu dem Ergebnis komme, daß sie ungleich sind, will ich mal denjenigen sehen, der mir da widerspricht

-
Plotzenhotz schrieb:
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?).
Nein, hast du nicht überlesen. Da müsste ich Euch die halbe Anwendung erklären. Glaubt mir einfach daß Boote und Fahrzeuge es genau so tun.
Plotzenhotz schrieb:
Ansonsten sehe ich das Problem nicht. Die Lösung heisst typeid. Guckst du:
(...)
Sehr gute Lösung, das habe ich gesucht. Meine bisherige Lösung ähnelt deiner ein wenig, aber deine hat einen großen Vorteile: Bei mir wäre für jede Vererbungsebene einmal eine RTTI-Operation gemacht worden (ich habe keine weitere, interne Vegleichsmethode benutzt). Darum wäre bei mir der dynamic_cast zwar einfacher gewesen, als in deinem Beispiel, aber eben verschachtelt. Deine Lösung ist viel besser.
Nebenbei gesagt, kannte ich die variante dynamic_cast<void*>() noch nicht. Asche auf mein Haupt.
Was das typeid anbelangt, frage ich mich, warum hast du den Vergleich nicht generell in operator ==() gemacht? Also so:
bool Vehicle::operator == (Vehicle const& other) const { if(typeid(other) == typeid(*this)) return CompareImpl(other); else return false; } // ....... virtual bool Car::CompareImpl(Vehicle const& other) const { assert(typeid(*this) == typeid(Car)); assert(typeid(other) == typeid(Car)); return CompareCars(static_cast<Car const&>(other)); }Das spart 'ne Menge Code, nicht? Habe ich was übersehen?
Plotzenhotz schrieb:
Funktioniert wunderbar. Und typeid(a) == typeid(b) würde ich immer einem dynamic_cast vorziehen, ganz einfach weil es schneller ist.
Es ist schneller? Hast du den Nerv mir das zu erklären?
Plotzenhotz schrieb:
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.
Daran hatte ich gedacht, aber die Objekte, in meinem Fall, sind (z.Zt.) nicht serialisierbar. Auch wäre diese Lösung wohl ziemlich langsam.
Plotzenhotz schrieb:
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.
Defaultwerte ist in der Tat dämlich. Da ich im Augenblick keine wirkliche Notwendigkeit habe, Elemente über Basiszeiger polymorph *zuzuweisen*, habe ich mich entschieden zu Clonen. Damit erledigen sich solche Zicken.
Ich habe für meinen polymorphen Container nur einen operator=() geschrieben der alle Elemente des Containers löscht und Clone der Elemente des zugewiesenen Containers einhängt.
Ich habe keine Lust, ohne echte Not "DerivedType = BaseType;" zu behandeln. Im Gegensatz zu "Auto == Schiff" muss ich nämlich den Vorredner recht geben, daß sich "Auto = Schiff" fürchterlich beißt.Du hast mir wirklich Klarheit geschaffen. Nicht nur deine == Lösung ist besser, ich bin jetzt auch sicher, daß es keine Lösung ohne RTTI gibt und daß ich nichts elementares übersehen habe. Vielen Dank!
-
Ja, ich war gedanklich viel zu wenig bei der RTTI geschichte.
Ich stell mich ja schon ins Eck

-
Jordy schrieb:
...
Plotzenhotz schrieb:
Funktioniert wunderbar. Und typeid(a) == typeid(b) würde ich immer einem dynamic_cast vorziehen, ganz einfach weil es schneller ist.
Es ist schneller? Hast du den Nerv mir das zu erklären?
...Ich bin zwar eigentlich nicht angesprochen, aber der Angesprochene schrieb hier:
Plotzenhotz schrieb:
...Ich weiss bloss dass MSVC strcmp() aufruft - und je tiefer er suchen muss, umso öfter. Und strcmp() ist halt langsamer als Hashcodes oder gleich UIDs zu vergleichen.
Ob das andere Compiler anders machen weiss ich nicht, kann leicht sein dass die meisten Compiler hersteller dynamic_cast nicht als wichtig genug sehen um das ordentlich zu optimieren.
Der Vergleich von type_info Instanzen ist übrigens auch über strcmp() implementiert, aber da wird wenigstens immer bloss ein String verglichen.
Gruß,
Simon2.
-
Jordy schrieb:
Plotzenhotz schrieb:
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?).
Nein, hast du nicht überlesen. Da müsste ich Euch die halbe Anwendung erklären. Glaubt mir einfach daß Boote und Fahrzeuge es genau so tun.
* Was ist das für eine Liste?
* Für welche Operation(en) brauchst du den Test auf Gleichheit?
* etc
-
Was das typeid anbelangt, frage ich mich, warum hast du den Vergleich nicht generell in operator ==() gemacht? Also so: (...)
Weil ich nicht daran gedacht habe bzw. es mir nicht aufgefallen ist

Das verwendete Pattern ist halt allgemein, und mir ist nicht aufgefallen dass ich ja nen Teil der Funktionalität in dem Fall in den operator == raufziehen kann. Normalerweise ist die public Funktion die die virtuelle private "Impl" Funktion aufruft ein dummer Stub der selber nix tut.Nochwas zur Frage warum type_info mit == zu vergleichen schneller ist als ein dynamic_cast: ich weiss nicht 100% ob das mit allen Compilern der Fall ist, aber ich denke bei type_info == vs. dynamic_cast *sollte* type_info == immer besser abschneiden. Einfach deswegen weil nur 2 konkrete Typen verglichen werden müssen, und nicht ein Typ in einer (möglicherweise recht langen) Liste von erreichbaren (und vom Ausgangspunkt aus eindeutig erreichbaren - vonwegen "multiple inheritance" ohne "virtual inheritance" und so - "the dreaded diamond") Basistypen gesucht werden muss. Selbst wenn man die Suche in der Liste irgendwie optimiert wird man es wohl kaum auf einen einzigen Vergleich/Lookup reduzieren können.
Was ich aber nicht ganz verstehe ist warum MSVC nicht wenigstens pre-hashed Strings hernimmt, wenn er schon unbedingt nen String Vergleich machen muss. Das ginge dann doch etwas flotter. Zwar nur eine lineare Optimierung, dafür sicher > Faktor 2 (wahrscheinlich eher Faktor 10 oder mehr für lange Namen die sich erst weit hinten unterscheiden).
Daran hatte ich gedacht, aber die Objekte, in meinem Fall, sind (z.Zt.) nicht serialisierbar. Auch wäre diese Lösung wohl ziemlich langsam.
Naja, wie langsam das ist kommt darauf an wie man serialisiert und wohin. Im Normalfall wirst du aber zumindest eine dynamische Speicheranforderung haben, und das kostet auch nicht gerade wenig. Allerdings wahrscheinlich auch nicht um Welten langsamer als die type_info Lösung.
ich bin jetzt auch sicher, daß es keine Lösung ohne RTTI gibt
Jain. Es gibt keine Lösung wo du nicht die Klassen irgendwie eindeutig identifizieren musst, was aber nicht unbedingt über RTTI sein muss. Eine einfache "virtual std::string GetClassName() const" Funktion würde schon ausreichen, bloss is das wieder viel Aufwand das überall nachzuziehen, und auch z.T. gefählich, denn wenn jmd. vergisst z.B. SportsCar::GetClassName zu deklarieren wird Car::GetClassName hergenommen, dann gibts ganz hässliche Probleme die u.U. sehr blöd zu debuggen sind. Siehe auch MFC, die tun auch fest rumcasten und vergleichen ohne RTTI, halt über das selbstgestrickte CRuntimeClass System.