Kosten von polymorphen Aufrufen - C ++

8

Ich schreibe ein Spiel in C ++, das ungefähr 30 verschiedene Rollen hat, die jeweils leicht unterschiedlich sind. Ich habe einen Benutzer der Hauptklasse, der alle Daten enthält, die von allen Rollen benötigt werden. Meine erste Implementierung beinhaltete nur eine Aufzählung von 30 Rollen und eine angemessene Handhabung, aber jetzt frage ich mich, ob es besser wäre, Benutzer als eine Basisklasse zu haben und jede Rolle eine eigene Klasse ist, die von Benutzer erbt.

Mein Hauptanliegen ist, wie effizient polymorphe Methodenaufrufe sind, wenn mehr als 30 Klassen von einer einzelnen Basisklasse erben? Ich weiß, dass polymorphe Aufrufe Zeiger in einer virtuellen Tabelle enthalten, aber ich bin mir nicht sicher, ob dies eine lineare Suche durch die gesamte Tabelle nach der richtigen Methode bedeutet oder ob sie in konstanter Zeit durchgeführt werden kann.

Wenn jemand die Effizienz von polymorphen Methodenaufrufen mit vielen vererbten Klassen kommentieren könnte, würde ich mich über die Aufklärung freuen.

Vielen Dank im Voraus!

    
Josh Brittain 15.04.2012, 23:27
quelle

3 Antworten

5

Die Kosten sind vernachlässigbar. Es spielt keine Rolle, wie viele Klassen Sie haben oder wie viele Vererbungsebenen, die Kosten eines polymorphen Aufrufs sind eine einfache Addition - der Zeiger auf vftable plus der Offset der spezifischen Funktion (nicht standardisiert, sondern aktiviert) die meisten, wenn nicht alle Implementierungen das ist richtig).

    
Luchian Grigore 15.04.2012, 23:31
quelle
7

Verwenden Sie nicht die Vererbung, sondern die Komposition. Geben Sie Ihrer Klasse 'Benutzer' ein Objekt, das die Rolle übernimmt. Sie können also 30 "Role" -Klassen erhalten. Aber sie erben nicht von 'Benutzer', sondern werden von 'Benutzer' verwendet (sie können ihre eigene abstrakte Basisklasse erben, um die Schnittstelle von 'Rolle' zu definieren)

oder wenn es nur eine Funktion ist .... könnten Sie es einfach als eine Menge von Funktionsobjekten modellieren und diese dann einfach an den Benutzer übergeben.

    
Keith Nicholas 15.04.2012 23:32
quelle
0

Leider ist die Antwort von Luchian Grigore falsch.

Bei Diamond-Vererbung wäre das korrekt. Wo Sie eine Klasse D haben, die von B und C erbt, die ihrerseits von A erbt. Wenn Sie eine Funktion von D aufrufen, wird es eine Additionsoperation für die V-Tabelle geben, um nach D-Referenz zu suchen.

%Vor%

In Ihrem Fall muss der Compiler für einen polymorphen Aufruf einen Lookup (Lesen) auf der vtable durchführen, um die richtige Funktion zu erhalten, die Sie aufrufen. Wenn es schlimmer wird, wenn die Daten, auf die die vtable verweist, nicht zwischengespeichert werden. In diesem Fall haben Sie einen Cache-Fehler , was sehr viel ist teuer.

Außerdem gibt es eine vtable pro Klasse und jedes Objekt dieser Klasse hat dieselbe vtable. Siehe In C ++, was ist eine vtable und wie funktioniert sie?

Die wirkliche Antwort auf Ihre Frage hängt davon ab, wie oft Sie zwischen diesen 30 Klassen wechseln und wie oft Sie sie verwenden werden. Wird es in einer Schleife verwendet?

Die Lösung, die Sie beschreiben, wird häufig verwendet, um dieses potenzielle Problem zu umgehen. Aber in der Tat, es könnte so schnell sein wie der polymorphe Anruf abhängig davon, wie es verwendet wird.

Also im Allgemeinen können Sie sich für die polymorphe Methode entscheiden, ohne sich Gedanken über die Kosten machen zu müssen, denn es wird nachlässig sein. Schreiben Sie zuerst sauberen und wartbaren Code, später optimieren Sie ihn. :)

BEARBEITEN:

Da es eine Diskussion über den tatsächlichen Compiler-Code in diesem Thread gab, entschied ich mich, ein Beispiel zu starten, um herauszufinden, was real passiert.

Unten sehen Sie die Beispielcode, den ich verwendet habe.

%Vor%

Dann können Sie den tatsächlichen Assembler-Code des Beispiels sehen (wie der Compiler es interpretiert hat).

%Vor%     
ForceMagic 17.04.2012 02:56
quelle

Tags und Links