Virtuelle Funktion wird verdeckt!



  • DStefan schrieb:

    Mit Humes Base und Derived:
    Ich bezweifle stark, daß das Sinn macht! Ein virtueller operator==() wird vermutlich mehr schaden als nützen.

    jo, scheint mir auch.
    also man darf schonmal nicht die rechte seite hochcasten, bis sie paßt, sie beliebig viel ballast abwerfen lassen und dann vergleichen.
    die beiden dürfen nur gleich sein, wenn sie genau der selben klasse angehören.

    bool operator==(Base const& a,Base const& b)
    { 
        if(typeid(a)!=typeid(b)) return false;
        if(!a.compare(b)) return false;
        return true;
    }
    

    kommt man hier auch billig ohne rtti weg?



  • Ich bezweifle stark, daß das Sinn macht! Ein virtueller operator==() wird vermutlich mehr schaden als nützen.

    Ich zweifle ebenfalls an dem Sinn. Ich wollte hier aber nicht über Sinn oder Unsinn diskutieren.

    Ich umgehe dieses Problem in der Regel dadurch, dass ich nur Value-Objekte mit dem op== vergleiche. Von diesen leitet man nicht ab und wenn, dann vergleicht man sie nicht polymporph. Insofern benutze ich weder virtuelle Vergleichsoperatoren noch virtuelle Zuweisungen.

    also man darf schonmal nicht die rechte seite hochcasten, bis sie paßt, sie beliebig viel ballast abwerfen lassen und dann vergleichen.
    die beiden dürfen nur gleich sein, wenn sie genau der selben klasse angehören.

    Man muss halt dafür sorgen, dass der Vergleich den Bedingungen einer Äquivalenzrelation genügt (Reflexiv, Symetrisch, Transitiv) und konsistent ist (ein wiederholter Vergleich liefert immer das gleiche Ergebnis, solange die beteiligten Objekte nicht veränert wurden).
    Meine Variante ist offensichtlich nicht symetrisch.

    Ein Vergleich, der Teilobjekt-Vergleiche (slice-comparison) erlaubt, kann auf der anderen Seite niemals Transitiv sein.

    Will man also unbedingt einen virtuellen op==, dann muss man bereits in der Basisklasse dafür sorgen, dass keine Teilobjekt-Vergleiche durchgeführt werden, dass also immer nur Objekte von exakt der gleichen Klasse verglichen werden. Um ein typeid (wie in volkards Lösung) kommt man nicht herum.
    Hat man einmal in der Hierarchie nur einen halbseitigen Test mit dem dynamic_cast drin (so wie bei mir), kann man die Symetrie-Bedingung nicht mehr erfüllen.

    PS: Wer das Ganze etwas ausführlicher haben will, sollte den folgenden Artikel lesen:
    http://www.cuj.com/documents/s=8467/cujjsup2004langer/

    Da geht's zwar um Javas equals, das Prinzip trifft aber auch auf C++ zu.



  • Lustig. Gerade laufe ich mal schnell runter zur Fluppenkiste. Schau auf dem Rückweg in den Briefkasten, fische die aktuelle Ausgabe des Dr. Dobb's Journal raus und was finde ich darin?

    Richtig: Einen Artikel mit dem Titel: "Overriding the C++ Operator==" 😃

    Muss ich doch gleich mal lesen...



  • TS++ schrieb:

    Hallo Bashar!

    Ich hab grad mal aus lauter Verzweiflung 'virtual' rausgeschmissen. Und was soll ich dir sagen: es funktioniert! 😕
    Das verwirrt mich jetzt doch etwas! Wieso ist denn sowas erlaubt? Ich hätte instinktiv niemals etwas derartiges programmiert!
    Vielleicht kannst du mir ja helfen!

    Danke!

    Grüße,
    TS++

    <imho>Dann dürfte die aber noch trotzdem überdeckt werden, du willst eine Überladung, dann brauchst du aber noch ein
    using base::method;
    davor.</imho>

    Lars



  • volkard schrieb:

    kommt man hier auch billig ohne rtti weg?

    Ich denke nicht. Das läßt sich im allgemeinen als Double-Dispatch bezeichnen. C++ kennt aber nur Single-Dispatch.
    Einfach mal nachgooglen, wenn jemand mit den Begriffen nichts anfangen kann...



  • @volkard:
    Ich glaube, du bist zu streng. Dein operator verlangt zwei Base und sollte sich meiner Meinung nach nicht darum scheren, ob seine Argumente "in Wirklichkeit" Unterklassen sind. Die Funktion sollte true liefern, wenn die Base-Anteile der Objekte gleich sind und Schluß.

    Ich gebe zu, daß es mir schwer fällt zu erklären, warum ich dieser Meinung bin. Im Moment fühlt sich das einfach nur richtig an.

    Vielleicht könnte man so sagen: Die Umgebung, in der dieser Vergleich stattfindet, wird wohl eine Base-Umgebung sein. Wenn Base Unterklassen hat, gibt es diese Base Umgebung als eine Art Framework für alles, was man mit Base machen kann. Ich unterstelle, daß dieses Framework per Design so geschaffen ist. Ein Test auf Gleichheit zweier Base _muß_ dann true liefern, falls der Base-Anteil der Objekte gleich ist, denn ich glaube, daß ein solches Framework anders gar nicht (richtig) funktionieren kann. Ein Test auf den _Wert_ eines Objekts sollte nicht vom _Typ_ des Objekts abhängen. Mit ist so, als würde ansonsten der Polymorphismus seine Unschuld verlieren.

    Ich habe bisher noch nie mit virtuellen operator==() experimentiert. Vielleicht ist dieser Beitrag deshalb ein bischen schwammig......

    Stefan.



  • Das würde ja bedeuten, dass ein Hund gleich einem Pinguin ist, wenn sie gleich alt sind?



  • Das würde ja bedeuten, dass ein Hund gleich einem Pinguin ist, wenn sie gleich alt sind?

    Nein, das würde bedeuten, daß zwei _Tiere_ gleich sind, wenn sie gleich alt sind. Und das unabhängig davon, ob es nun Hunde oder Pinguine sind. Das habe ich mit diesem Begriff "Framework für Base" gemeint. Im Kontext des operator==(const Tier&, const Tier&) gibt es weder Hunde noch Pinguine, bloß Tiere.

    Stefan.



  • Fazit:
    Die Implementierung ist Kontexabhängig.



  • Scheiss neues Forum, immer wieder ausgeloggt. 😡



  • Ich hab dich schon verstanden, ich finde es nur sinnlos, dass du dem Vergleichsoperator willkürlich einen solchen eingeschränkten Kontext geben willst.

    Hund h("Collie", 42);
    Pinguin p(42);
    assert(h == p);
    

    finde ich fragwürdig.



  • Hund h("Collie", 42);
    Pinguin p(42);
    assert(h == p);

    C/C++ Code:
    .........

    finde ich fragwürdig.

    Sehe ich genau so.



  • Es kommt auf den Zusammenhang an. Die Frage kann man nicht mit Hunden und Pinguinen klären. Es wär doch möglich, dass man einmal einen Fall hat wo es Sinn macht.



  • Eine goldene Regel in C++ ist, das Operatoren genau das tun sollten, was von ihnen erwarte wird. Dieses gilt dann natürlich auch für den operator == (). Soll dieses weiterhin als Regel gelten, ergeben sich daraus einige Konsequenzen.

    Als erste Möglichkeit definieren wir mal den operator == () nur für eine Basisklasse und nicht für die abgeleiteten Klassen. Ich vergleich z.B. zwei Tiere - wenn sie gleich alt sind gibt es ein true. Das hätte zur folge, das wenn ich ein Pinguin und einen gleich alten Waschbären vergleichen würde, diese auch gleich wären (wie unten schon beschrieben) => Als Außenstehender würde ich das nicht erwarten. Also sollte man das vermeiden. Als Lösung für eventuell doch notwendige Vergleiche kann mann immer noch explizite Vergleichsfunktionen definieren. Den Algorithmen der STL kann man fast immer soch einen Vergleich explizit angeben.

    Die zweite Möglichkeit ist dann der virtuelle operator == (), der überschrieben wird. Hier wäre es ohne weiteres zunächst möglich, ob das übergebene Objekt vom gleichen Typ wie das erste Argument ist (RTTI -> wie unten beschrieben). Danach kann eine Typenumwandlung (gefahrlos) erfolgen und ein tiefgehender Vergleich angestellt werden. In diesem Fall ist das Verhalten auf jeden Fall erwartet. Es gibt nur ein true zurück, wenn es sich tatsächlich um Gleichheit handelt (wenn die Implementierung auch soweit in Ordnung ist).

    Dann ergibt sich noch die Frage nach dem Sinn eines virtuellen operators == (). Ein Einsatzbeispiel wären (oder besser sind) virtuelle Iteratoren. Will man zwei virtuelle Iteratoren Vergleichen, so muß das tatsächliche Typ auch übereinstimmen. Für Iteratoren ist der operaror == () außerdem zimlich wichtig, wenn man sie standardmäßig benutzen will.

    Übrigens ist der Einsatz von RTTI in diesem Fall zimlich ungefärlich. Es wird nur einmal der Typ abgefragt. RTTI führt hier nicht zu switch-konstrukten und deren Problemen.



  • @Bashar:
    Dein Beispiel ist für mich genauso fragwürdig wie für dich. Allerdings trifft es (*hüstel*) nicht das Problem, um das es hier geht.

    Der springende Punkt ist doch (so habe ich das zumindest verstanden), daß operator==() für Oberklassen (also z.B. für Tier) aufgerufen wird. Und durch die Beispiele wurde zumindest implizit klar, daß alle Klassen dieser Hierarchie einen operator==() implementieren. Unter diesen Voraussetzungen schlägt deine Assertion fehl - und das sollte sie auch.

    Stefan.


Anmelden zum Antworten