Theortische Frage zur Implementierung von virtuellen Methoden
-
Das 'Problem' das ich hier sehe ist: du kannst diesen 'doppelzeiger' nicht in ein register geben (wenn wir jetzt von einem normalen ia32 ausgehen). Somit könntest du einen methodenaufruf nicht so schön über ein register laufen lassen, da du ja 2 register brauchst. das sind pro aufruf doppelte kosten
Angenommen ich habe folgendes:
class ABC_Foo { public: virtual void foo () = 0; }; protocol Protocol_Foo void foo (); }; class ABC_Bar : public ABC_Foo { public: void foo () { ... } }; class Protocol_Bar : public Protocol_Foo { public: void foo () { ... } }; int main { ABC_Bar abc_bar; Protocol_Bar protocol_bar; ABC_Foo * abc_foo = &abc_bar; // gewöhnlicher Zeiger Protocol_Foo * protocol_foo = &protocol_bar; // großer Zeiger abc_foo->foo(); // *1 protocol_foo->foo(); // *2 }
Angenommen der Optimizer merkt nicht, das der Typ auch hier eigentlich schon zur Compilezeit feststeht.
In *1 habe ich eine Indirektion über den Zeiger 'abc_foo' und eine weitere über die Methodentabelle. Ich habe also also zwei Zeiger mit denen ich umgehen muss.
In *2 habe ich eine Indirektion über den Methodenzeiger. Die Adresse des Objekts brauche ich, damit Sie als this an die Methode übergeben kann. Ich hantiere also ebenfalls mit zwei Zeigern. Aber der Zeiger aufs Objekt muss gar nicht dereferenziert werden, da er lediglich als this an die Methode übergeben wird.Ein großer Zeiger ist ja nicht anderes als zwei Zeiger.
Alternative Frage: kann man mit den heutigen generischen Sprachbestandteilen etwas bauen, das dem großen Zeiger nahe kommt?
-
Du musst NonVirtual jetzt als Doppelzeiger speichern. Somit wäre dein Ansatz sicher langsamer bei nicht virtuellen Funktionen (und genau darum geht es bei C++ ja: keine unnötigen kosten).
Ich hab meinen Vorschlag ja schon so angepasst, das nur Instanzen der in dem zusammenhang eingefürten Protokolle große Zeiger bzw. Referenzen sind.
Dein Code würde folglich sich genau so verhalten, wie bisher.
Auch ABCs kann man ja weiterhin verwenden, sollten Sie Vorteile bieten.
-
a) die doppelte indizierung ist heutzutage hinreichend schnell, im idealfall genauso schnell wie einfache indizierung. Problematisch ist hier eher der Sprung an eine schwer vorhersagbare Adresse - und die hat man in beiden Fällen.
b) Jede Membermethode erhält (versteckt) einen "this"-Zeiger. Bei einer Klasse mit mindestens einer virtuellen Methode muß an *jede* Methode der Doppel-Zeiger weitergegeben werden. Kann höchstens für inline-Funktionen wegoptimiert werden.
c) hat mindestens genausoviele Zeiger auf (polymorphe) Objekte wie Objekte selbst - häufig jedoch mehr. Das Platzpar-Argument zieht also nicht.
(gut, es lassen sich Fälle konsttruieren, in denen man mehr polymorphe Instanzen als Zeiger hat. Ist aber hinreichend selten. Und wenn hier Platz wirklich eine Rolle spielt, könnte man das mit einer Proxy-Klasse erledigen, ist aber häßlich und aufwendig)
Wenn ich so drüber nachdenke, ist c) praktisch das Auskriterium. Aber trotzdem
d) Die Idee gefällt mir, und war auf jeden Fall eine gute frage
-
Helium schrieb:
nur beim bestimmen der Adresse wäre es nötig (also bei &objek). Oder sehe ich das falsch?
Ne, denke ich auch. Nur woher kriegen wir bei &objek die Info? Das objekt weiß es ja jetzt nicht mehr.
MfG Jester
-
Jester schrieb:
Helium schrieb:
nur beim bestimmen der Adresse wäre es nötig (also bei &objek). Oder sehe ich das falsch?
Ne, denke ich auch. Nur woher kriegen wir bei &objek die Info? Das objekt weiß es ja jetzt nicht mehr.
MfG Jester
Vergiss meine erste Aussage. Den Typ in deisem Fall kennt der Compiler:
Typ objekt; EinProtocol * foo = &objekt; // Welchen Typ hat objekt wohl.
c) hat mindestens genausoviele Zeiger auf (polymorphe) Objekte wie Objekte selbst - häufig jedoch mehr. Das Platzpar-Argument zieht also nicht
Hat man? Wieso sollte man?
EinProtokol * foo = new ImplementiertEinProtokol[1000000];
Hier habe ich schon 999999 Zeiger weniger. :p
Ich lege sehr viele Objekte auf dem Stack an. ERst durch die Übergabe an eine Funktion, die eine Referenz auf eine Basisklasse erwartet kommen die polymorphen Eigenschaften zu Zuge.
d) Die Idee gefällt mir, und war auf jeden Fall eine gute frage
Danke
Naja, war nur so eine spontane Idee. Ich habe mir nicht wirklich ernsthaft Gedanken gemacht. So wie es aussieht ist es nicht besonders sinnvoll. Schade.
-
EinProtokol * foo = new ImplementiertEinProtokol[1000000];
Mußt du genau den Fall nehmen, den ich als "hinreichend selten" aussortiert habe??
Die Polymorphie macht hier ja nur sinn, wenn man wenigstens zwei solche Arrays hat, und eine Funktion die immer ein Objekt davon ausgibt. In den meisten Fällen hat man dann ja doch ein Array von EinProtokol-Zeigern auf verschiedene Implementationen.
-
Wenn ich mehrere Zeiger auf das selbe Objekt habe zahle ich aber mehrfach...
Das Problem ist damit nur verlagert.MfG Jester
-
statt einer tabelle nen entscheidungsbaum nehmen. so wie in eiffel halt. macht viel oft schnell wegen inlining.
das problem an virtuellen funktionen ist ja minder die aufrufgeschwindigkeit, sondern daß der optimierer dort blind ist.
-
Entscheidungsbaum heißt über RTTI oder sonstwie die "richtige" Klasse rauskriegen und mit einem switch / if direkter Aufruf??
-
peterchen schrieb:
Entscheidungsbaum heißt über RTTI oder sonstwie die "richtige" Klasse rauskriegen und mit einem switch / if direkter Aufruf??
ja.