Ich weiß, dass abgeleitete Klassen das Basisklassenmitglied einfach "neu definieren" können Funktionen, und das, wenn diese Funktion eines abgeleiteten Klassenobjekts ist aufgerufen, wird die in der abgeleiteten Klasse definierte Funktion verwendet, aber ... Macht das das "virtuelle" Schlüsselwort nicht überflüssig? Ich habe gelesen einige offensichtlich signifikante Unterschiede zwischen diesen beiden Fällen (dh: wenn Sie haben einen Basisklassenzeiger, der auf eine abgeleitete Klasse zeigt und Sie aufrufen eine Funktion, wenn es sich um eine virtuelle Funktion der abgeleiteten Klasse handelt aufgerufen, aber wenn nicht, wird die Basisklassenfunktion aufgerufen).
Anders gesagt, was ist der Zweck, Mitglied neu definieren zu können? Funktionen als nicht-virtuelle Funktionen, und dies ist eine häufig verwendete üben?
Persönlich scheint es mir, als würde es nur sehr verwirrend werden.
Danke!
Der gebräuchlichste Ansatz für die gebräuchlichsten OOP-Sprachen (Java, SmallTalk, Python usw.) besteht darin, standardmäßig jede Mitgliedsfunktion als virtual
zu haben.
Der Nachteil ist, dass es bei jedem virtuellen Anruf zu einer kleinen Leistungseinbuße kommt. Aus diesem Grund können Sie in C ++ auswählen, ob Ihre Methoden als virtuell definiert werden sollen oder nicht.
Es gibt jedoch einen sehr wichtigen Unterschied zwischen einer virtuellen und einer nicht-virtuellen Methode. Zum Beispiel:
%Vor% Der tatsächliche Code, der beim Aufruf von someVirtualMethod
ausgeführt wird, hängt vom konkreten Typ des referenzierten Zeigers p
ab und hängt vollständig von SomeClass
subclasses redefinition ab.
Aber der für den someNonVirtualMethod
-Aufruf ausgeführte Code ist eindeutig: immer der für SomeClass
, da der Typ der p-Variablen SomeClass
ist.
Es klingt so, als ob Sie bereits den Unterschied zwischen virtuellen und nicht-virtuellen Methoden kennen, also werde ich nicht darauf eingehen, wie andere es haben. Die Frage ist: Wann wäre eine nicht-virtuelle Methode nützlicher als eine virtuelle?
Es gibt Fälle, in denen Sie nicht möchten, dass ein vtable-Zeiger in jedem Objekt enthalten ist. Sie sollten also sicherstellen, dass keine virtuellen Methoden in der Klasse vorhanden sind. Nehmen Sie zum Beispiel eine Klasse, die einen Punkt darstellt und zwei Mitglieder hat, x
und y
- Sie könnten eine sehr große Sammlung dieser Punkte haben, und ein Vtable-Pointer würde die Größe des Objekts um mindestens 50% erhöhen.
Adressierung: "Anders ausgedrückt, was ist der Zweck, Memberfunktionen als nicht-virtuelle Funktionen neu zu definieren, und ist dies eine häufig verwendete Praxis?"
Nun, Sie können nicht. Wenn die Basisklassenmethode virtuell ist, wird die entsprechende abgeleitete Klassenmethode verwendet, unabhängig davon, ob das Schlüsselwort 'virtual' verwendet wird oder nicht.
Also: "Macht das das" virtuelle "Schlüsselwort nicht überflüssig?". Ja, es ist in der abgeleiteten Klassenmethode redundant, aber nicht in der Basisklasse.
Beachten Sie jedoch, dass es ungewöhnlich (höflich) ist, eine nicht-virtuelle Methode zu haben und sie dann als abgeleitete Klasse neu zu definieren.
Es wird für eine Instanz der abgeleiteten Klasse und einen Zeiger auf die abgeleitete Klasse funktionieren. Wenn Sie jedoch Ihre abgeleitete Klasse in eine Funktion übergeben, die einen Zeiger auf Base verwendet, wird die Base-Version der Funktion aufgerufen. Dies ist wahrscheinlich nicht wünschenswert. Zum Beispiel gibt der folgende 5 zurück
Dies liegt an der Funktionsweise von virtual. Wenn ein Objekt einen virtuellen Aufruf hat, wird die korrekte Funktion in der V-Tabelle nachgeschlagen, wenn Sie die virtuelle Funktion aufrufen. Andernfalls haben Sie keine Vererbung, der Base-Pointer verhält sich wie die Basisklasse und nicht wie die abgeleitete Klasse.
Es ist wichtig, vorsichtig zu sein, wenn Destruktoren in abgeleiteten Klassen als nicht virtuell markiert und "überschrieben" werden - es ist möglich, dass die Klasse nicht korrekt bereinigt wird, wenn der Destruktor in einem Zeiger auf die Basisklasse aufgerufen wird. p>
scheint, dass Sie die Macht des Polymorphismus nicht entdeckt haben. Polymorphismus funktioniert auf diese Weise:
Sie haben eine Funktion, die einen Zeiger einer Basisklasse akzeptiert. Sie können ihn aufrufen, indem Sie abgeleitete Klassenobjekte entsprechend der unterschiedlichen Implementierung der abgeleiteten Klasse übergeben. Die Funktion verhält sich dann anders. Es ist ein wunderbarer Weg, weil die Funktion stabil ist, selbst wenn wir die Hierarchie der Vererbungsstruktur erweitern.
Ohne diesen Mechanismus können Sie dies nicht erreichen. Und dieser Mechanismus benötigt "virtuell"
C ++ Scoping und Namens-Lookup-Regeln erlauben sehr seltsame Dinge, und Methoden sind hier nicht allein. Tatsächlich kann Verbergen (oder Spiegeln ) in vielen verschiedenen Situationen auftreten:
%Vor%Warum dann? Für Ausfallsicherheit.
Wenn ich eine Base
-Klasse modifiziere, kenne ich nicht alle möglichen Kinder. Wegen der versteckten Regel (und trotz der Verwirrung, die es verursachen kann), kann ich ein Attribut oder eine Methode hinzufügen und es ohne eine Sorgfalt in der Welt verwenden:
Base
auf, egal ob ein Kind sie überschreibt oder nicht Es ist offensichtlich, dass wenn Sie das Programm als eine endliche Arbeit betrachten, es keinen Sinn ergibt, sondern Programme sich entwickeln und die Versteckregel die Evolution erleichtert.
Der einfachste Weg, es zu erklären, ist wahrscheinlich folgendes:
Virtual sucht nach Ihnen, indem Sie eine virtuelle Nachschlagetabelle hinzufügen.
Mit anderen Worten, wenn Sie das virtuelle Schlüsselwort nicht hätten und eine Methode überschreiben würden, müssten Sie diese Methode immer noch manuell aufrufen [verzeihen Sie, wenn mein Gedächtnis für C ++ - Syntax ein wenig rostig ist]:
%Vor%Sie könnten dies ein wenig optimieren (am besten für Lesbarkeit UND Leistung), indem Sie eine Nachschlagetabelle verwenden:
%Vor%Nun, das ist eher ein Ansatz auf hoher Ebene. Der C ++ - Compiler kann dies offensichtlich ein wenig mehr optimieren, indem er genau weiß, was "type_info" ist, und so weiter. Also wahrscheinlich, statt dieser "map" und der Suche "methodToCall = aDoSomethingVTable [typeid (thing)]", dann rufen Sie, "der Compiler fügt etwas viel kleiner und schneller, wie" doSomethingWithAnA * A_doSomethingVTable; "gefolgt von" A_doSomethingTablething - & gt; Typnummer ".
Sie haben also Recht, dass C ++ nicht wirklich virtuell benötigt, aber es fügt viel syntaktischen Zucker hinzu, um Ihr Leben einfacher zu machen, und kann es auch besser optimieren.
Das heißt, ich denke immer noch, dass C ++ eine schrecklich veraltete Sprache ist, mit vielen unnötigen Komplikationen. Virtuell könnte zum Beispiel (und wahrscheinlich sollte sollte sein) standardmäßig angenommen werden und wo unnötig optimiert werden. Ein Scala-ähnliches "override" -Schlüsselwort wäre viel nützlicher als "virtual".
Tags und Links c++ inheritance virtual-functions