Überschriebene virtuelle Funktion: virtuell oder nicht?
-
Sowohl
class Base { virtual void foo() {std::cout << "base";} }; class Derived : Base { void foo() {std::cout << "derived";} };als auch
class Base { virtual void foo() {std::cout << "base";} }; class Derived : Base{ virtual void foo() {std::cout << "derived";} };überschreibt in Derived die Funktion Base::foo.
In einer Klasse Derived2, die von Derived ableitet, könnte ich ja nach wie vor foo von Base überschreiben. Haben diese beiden Syntaxen einen praktischen Unterschied?
-
Kaktus schrieb:
Haben diese beiden Syntaxen einen praktischen Unterschied?
Js. Man sieht sofort beim Blick auf Derived, dass foo virtual ist und muss sich nicht durch die gesamte Vererbungshierarchie hangeln, um zu gucken, ob irgendwo ein virtual steht.
-
Danke. Aber technisch sind sie identisch?
-
Kaktus schrieb:
class Base { virtual void foo() {std::cout << "base";} }; class Derived : Base { void foo() {std::cout << "derived";} };Weil foo in Base virtual ist, bekommt foo in Derived automatisch das virtual spendiert, ohne dass du es angegeben hast.
-
Oder : einmal virtuell, immer virtuell. Trotzdem sollte es hingeschrieben werden.
-
Kaktus schrieb:
Danke. Aber technisch sind sie identisch?
Ja.
-
Alternativ könnte man auch das override keyword benutzen (falls verfügbar), das würde für mich mehr Sinn ergeben.
Ist es allerdings nicht so, dass ein Aufruf von foo einer von Derived abgeleiteten Klasse mit einem Pointer auf Derived im ersten Beispiel Derived::foo aufruft, statt die Funktion der von Derived abgeleiteten Klasse? Im zweiten Beispiel sollte es doch korrekt laufen, oder irre ich mich da? Ist schon eine Weile her.
-
Cachus schrieb:
Ist es allerdings nicht so, dass ein Aufruf von foo einer von Derived abgeleiteten Klasse mit einem Pointer auf Derived im ersten Beispiel Derived::foo aufruft
Meinst du das?
class Base { virtual void foo() {std::cout << "base";} }; class Derived : Base { void foo() {std::cout << "derived";} };Nö. Einmal virtuell, immer virtuell.
#include <iostream> struct Base { virtual void foo() {std::cout << "base";} }; struct Derived : Base { void foo() {std::cout << "derived";} }; struct SecondDerived : Derived { void foo() {std::cout << "second-derived";} }; int main() { Base* a = new SecondDerived; a->foo(); }
-
Cachus schrieb:
Alternativ könnte man auch das override keyword benutzen (falls verfügbar), das würde für mich mehr Sinn ergeben.

Es gibt auch Compiler-Fehler, falls keine Funktion überschrieben werden kann. Allerdings fehlt mir der GCC-Support (wenn ich mich da nicht irre).
-
Ich meinte das eher so:
class Base { public: virtual void foo() {std::cout << "Base";} }; class Derived : public Base { public: void foo() {std::cout << "Derived";} }; class Derived2 : public Derived { public: void foo() {std::cout << "Derived 2";} }; int main(int argc, char *argv[]) { Derived2 d2; Derived* d = &d2; d->foo(); }Das gibt bei mir tatsächlich korrekt "Derived 2" aus, ist wohl doch schon länger her als ich dachte.
-
Hi,
wird eine Basisklasse angelegt die mindestens einen virtuellen Member hat,
so erzeugt der Kompiler für die Basisklasse, und folgend alle abgeleiteten
Klassen, eine Tabelle mit Addresseinträgen der, in der Basisklasse als virtuell
deklarierten, Methoden (auch virtuelle Tabelle genannt).Wird ein Derivat instanziiert bekommt diese die virtuelle Tabelle mit den
aktuellen Adresseinträgen (Im Basisklassensegment). Diese Tabelle entspricht auch einer Jumptable.Beispiel:
(skizzenhaft)class Base { public: virtual void foo() { __debugbreak(); }; void bar() { __debugbreak(); }; }; class Derived:public Base { void foo () { __debugbreak(); }; void bar() { __debugbreak(); }; }; void func_a(Base& blub) { blub.foo(); // wird durch derived ausgeführt blub.bar(); // wird durch base ausgeführt }; int _tmain(int argc, _TCHAR* argv[]) { Base b; Derived d; func_a(d); __debugbreak(); return 0; }Der Vorteil von Virtuellen Klassen ist, das diese überladene Methoden
des Derivats aus der Basis heraus aufrufen kannst, und das nur durch
die virtuelle Tabelle.
Die virtuelle Tabelle liegt vor dem eigentlichen datensegment
der instanz, also vor dem ersten Member.grüße
-
zeusosc schrieb:
VirtuellenKlassenPolymorphen Klassen
-
Kurze Ergänzung: Die "virtuelle Tabelle" nennen AFAIK die meisten Compiler
vtable, die fachsprachliche Abkürzung ist VMT (VirtualMethodTable). Einige Compiler liefern Fehler wieundefined reference to `vtable for VintagePort'
-
Japp und Japp

-
Hoffentlich gibts im neuen C++ Standard eine Möglichkeit nur Entpolymorphisierung.
-
Du meinst, eine virtuelle Funktion in abgeleiteten Klassen nicht mehr virtuell zu machen? Falls ja, was sollte das bringen?
-
Weil nicht-polymorphistische Methoden schneller im Aufruf sind.
-
xcall schrieb:
Weil nicht-polymorphistische Methoden schneller im Aufruf sind.
Nicht, wenn du es deinem Compiler nicht explizit verbietest, schlau zu sein.
Der gcc macht mir die virtuelle Funktion sogar inline, wenn er schnallt, dass kein late binding stattfindet. Das fand ich echt krass.
-
xcall schrieb:
Weil nicht-polymorphistische Methoden schneller im Aufruf sind.
Du kannst aber dennoch nicht einfach die Semantik ändern. Wenn die Funktion in der abgeleiteten Klasse nicht mehr virtuell wäre, aber über eine Basisklasse aufgerufen würde, was sollte dann passieren?
Erkläre das Ganze bitte ausführlicher. Bisher macht es eher den Anschein, dass du ein total unnützes Feature verfechtest, nur weil dir Performance-Mythen Angst machen.
-
Nexus schrieb:
xcall schrieb:
Weil nicht-polymorphistische Methoden schneller im Aufruf sind.
Du kannst aber dennoch nicht einfach die Semantik ändern. Wenn die Funktion in der abgeleiteten Klasse nicht mehr virtuell wäre, aber über eine Basisklasse aufgerufen würde, was sollte dann passieren?
Erkläre das Ganze bitte ausführlicher. Bisher macht es eher den Anschein, dass du ein total unnützes Feature verfechtest, nur weil dir Performance-Mythen Angst machen.
Ich denke, es ist so gemeint, wie ich es ursprünglich im Kopf hatte: Dass der virtuelle Aufruf nur stattfindet, wenn die Funktion in Klasse X des Pointers auf X noch virtuell ist, was dem Compiler als Optimierungshilfe dient. Es macht ja wenig Sinn, einen virtual function call durchzuführen, wenn bekannt ist, dass es keine überschreibung gibt.
Ist allerdings fragwürdig, ob das allein ausreicht um so ein Feature einzuführen.