abstrakte Klassen
-
HI
Mich würde mal interessieren ob abstrakte Klassen in der Art
virtual tuWas()=0;
und virtuelle Methoden in der Art
virtual tuWas() {};
Speicherplatz verbrauchen ?
MFG
xmarvel
-
ersteres verbraucht soweit ich weis keinen speicher letzteres schon
-
In beiden Fällen wird der Funktions-Zeiger in der vtable hinterlegt.
-
In beiden Fällen wird der Funktions-Zeiger in der vtable hinterlegt.
was ist ein vtable ?
Meinst du das otze recht hat oder meinst du das beide keinen Speicherplatz verbrauchen ?MFG
xmarvel
-
er meint, das ich falsch liege, womit er sogar recht hat, die vtable hab ich ganz übersehen^^
-
Also beide Methode verbrauchen Speicherplatz.
Jetzt noch ne kleine Frage beim der zweite Methoden warnt mich mein Compiler das er ein virtuellen Destruktor braucht bei der ersten Methode nicht. Also braucht man bei der abstrakten Methode keinen virtuellen Destruktor oder ?MFG
xmarvel
-
Ein vtable ist eine Liste von Funktionspointern, die Klassen mit virtuellen Funktionen haben, um darüber auf überladene Funktionen zugreifen zu können.
Und ein virtueller Dtor wird immer gebraucht, wenn eine Klasse einen virtuellen Member hat, also auch bei abstrakten Klassen
-
xmarvel schrieb:
was ist ein vtable ?
vtable oder virtual table ist eine Liste von Pointern auf alle virtuellen Funktionen.
Wenn du vor hast, die Klasse über den Basisklassenzeiger zu löschen, dann muss der dtor virtual sein, ansonsten würde ich ihn protected machen.
-
ah gut wieder was dazugelernt thx
MFG
xmarvel
-
otze schrieb:
er meint, das ich falsch liege, womit er sogar recht hat, die vtable hab ich ganz übersehen^^
Nein da hat er nicht recht, eine abstrakte Klasse kann nicht instanziert werden und somit wird auch kein Vtable für diese Klasse gebraucht.
-
*** schrieb:
Nein da hat er nicht recht, eine abstrakte Klasse kann nicht instanziert werden und somit wird auch kein Vtable für diese Klasse gebraucht.
Ach, das heißt, dass bei der Instanziierung einer abgeleiteten Klasse wird der ctor folgender Klasse auch nicht aufgerufen und somit keine vtable erstellt, erzähl mal:
class A { public: A() { } virtual ~A() { } void foo() = 0; };
-
Ach, das heißt, dass bei der Instanziierung einer abgeleiteten Klasse wird der ctor folgender Klasse auch nicht aufgerufen und somit keine vtable erstellt, erzähl mal:
Dass der Ctor nicht aufgerufen wird hab ich nie behauptet und wieso man ohne Vtable kein Ctor aufrufen können soll ist mir auch völlig schleierhaft. Der wird schließlich statisch vom (default) Ctor der abgeleiteten Klasse aus aufgerufen. Genauso wird doch auch der Vtable pointer gesetzt.
Ein guter Compiler wird dir den Pointer nur im Ctor der am abgeleitetesten Klasse setzen.
Ein normaler Compiler wird ihn im Ctor von jeden nicht abstrakten Klasse setzen, im Ctor einer abstrakten Klasse macht es wenig Sinn denn es kommt ja sowieso noch ein andere Ctor welcher den Wert eh überschreiben würde.
Ein grotten schlechter Compiler wird dir auch für abstrakte Klassen ein Vtable basteln, der aber nie gebraucht werden wird.
-
Das heißt, eine vtable wird auf jeden Fall angelegt, auch wenn nur in der abgeleiteten Klasse, die instanziiert wird. Somit bestätigt sich meine Aussage.
-
Shlo schrieb:
Das heißt, eine vtable wird auf jeden Fall angelegt, auch wenn nur in der abgeleiteten Klasse, die instanziiert wird. Somit bestätigt sich meine Aussage.
Ah jetzt seh ich worauf du hinaus willst aber da liegst du falsch:
class A{ public: virtual void foo()=0; }; class B:public A{ public: void foo(); };
Hier wird nur ein Vtable angelegt und zwar für B. Und da ist ein Pointer auf B::foo und nicht A::foo. Es kann kein Pointer auf A::foo geben da es diese Funktion ja nicht definiert sein muss.
Wenn du nun argumentierst, dass B::foo nur im Vtable ist weil A::foo pure virtual ist dann ist deine Ausage trotzdem nicht richtig da du dich in deiner Ausage auf A::foo beziehst:
xmarvel schrieb:
Mich würde mal interessieren ob abstrakte Klassen in der Art[...] und virtuelle Methoden in der Art[...] Speicherplatz verbrauchen ?
Shlo schrieb:
In beiden Fällen wird der Funktions-Zeiger in der vtable hinterlegt.
Da du von keinem anderen Funktions-Zeiger sprichts kann sich "der Funktions-Zeiger" nur auf xmarvels Frage beziehn. Wenn du jetzt "ein" anstatt "der" meintst (wie du in es in deinem letzten Post machst) ist es immer noch nicht richtig da es beliebig viele abgleitete Klassen geben kann (auch gar keine) und für jede wird ein Vtable angelegt und Speicher gebraucht. Also richtet sich der Speicherverbrauch nach der Anzahl der abgleiteten Klassen und nicht danach, dass es eine pure virtuale Funktion gibt.
Shlo schrieb:
Das heißt, eine vtable wird auf jeden Fall angelegt
Wie bereits gesagt muss es überhaupt keinen Vtable geben.
-
Dass für jede abgeleitete Klasse eine vtable angelegt wird, halte ich für unlogisch und sehr unwahrscheinlich. Es würde reichen wenn die Basisklasse eine vtable enthält und jede weitere abgeleitete Klasse die Zeiger auf virtuelle Funktionen in diese vtable reinschreibt. Ich hoffe jemand kann deine Aussage bestätigen, ansonsten sind es nur reine Spekulationen...
-
Shlo schrieb:
Dass für jede abgeleitete Klasse eine vtable angelegt wird, halte ich für unlogisch und sehr unwahrscheinlich. Es würde reichen wenn die Basisklasse eine vtable enthält und jede weitere abgeleitete Klasse die Zeiger auf virtuelle Funktionen in diese vtable reinschreibt. Ich hoffe jemand kann deine Aussage bestätigen, ansonsten sind es nur reine Spekulationen...
Der Standard schreibt hier nichts vor aber alle Compiler die ich kenne erstellen für jede Klasse ein eigenes Vtable, und aus Performance und Codegröße kenne ich eigentlich kein anderen Weg das zu machen.
Um Instancen klein zu halten enthalten sie legendlich einen Pointer auf ein Vtable das alle Instancen dieser Klasse (und nur exakt dieser Klasse keine verwanten Klassen) teilen. Ein Vtable ist ein ganz gewöhnliches Array das aus Funktionspointern besteht und dessen Größe at runtime bekannt ist. In den Vtables verwanter Klassen befinden sich virtuelle Funktionen mit gleichem Namen jeweis am gleichen Offset. Wenn du nun einen virtuellen Funktionsaufruf machst dann gehst du at runtime eigentlich zuerst den Vtablepointer deiner Instance dereferenzieren anschließed gehst du an den Offset an der dein Funktionspointer steht (er steht ja bei jedem verwanten Vtable am gleichen Offset also spielt die Klasse deiner Instance keine Rolle). Und als letztes rufst du die Funktion auf dessen Pointer du gerade gefunden hast.
Der Vtablepointer wird aber nicht nur zum aufrufen einer virtuelen Funktion gebraucht sondern er dient auch als Identifikationsnumber beim Downcasten.
Tja schon wieder falsch würde ich sagen
-
*** schrieb:
Nein da hat er nicht recht, eine abstrakte Klasse kann nicht instanziert werden und somit wird auch kein Vtable für diese Klasse gebraucht.
Doch, und zwar für den Kon- und Destruktor. In den allermeisten Fällen kann der aber theoretisch wegoptimiert werden (VC kann man mit __declspec(novtable) unter die Arme greifen).
class A; void DoBadThingsToA(A* a); class A{ public: A() { DoBadThingsToA(this); } virtual void foo() { cout << "foo" << endl; } virtual void bar() = 0; }; class B:public A{ public: virtual void foo() { cout << "foo2" << endl; } virtual void bar() { cout << "bar2" << endl; } }; void DoBadThingsToA(A* a) { a->foo(); // OK a->bar(); // Undefiniertes Verhalten }
(wobei DoBadThingsToA natürlich beliebig kompliziert sein kann)
*** schrieb:
Ein guter Compiler wird dir den Pointer nur im Ctor der am abgeleitetesten Klasse setzen.
... sofern er absolut sicherstellen kann, dass derweil niemand über einen Zeiger auf ein A* virtuelle Funktion aufrufen kann.