Vermeiden von dynamic_cast / RTTI

8

Ich habe kürzlich an einem Stück C ++ - Code für ein Nebenprojekt gearbeitet (die cpp-markdown -Bibliothek , z der Neugierige) und stieß auf eine Coding-Frage, über die ich gerne ein paar Meinungen hätte.

cpp-markdown hat eine Basisklasse namens Token , die eine Reihe von Unterklassen hat. Zwei der Hauptunterklassen sind Container (enthält Sammlungen anderer Token s) und TextHolder (wird als Basisklasse für Token s verwendet, die natürlich Text enthalten).

Der Großteil der Verarbeitung wird über virtuelle Funktionen abgewickelt, aber einige davon wurden besser in einer einzigen Funktion behandelt. Dafür habe ich dynamic_cast benutzt, um den Zeiger von Token* auf eine seiner Unterklassen down-casten zu lassen, damit ich Funktionen aufrufen konnte, die spezifisch für die Unterklasse und ihre Kindklassen sind. Es besteht keine Chance, dass der Casting fehlschlägt, weil der Code über virtuelle Funktionen (wie isUnmatchedOpenMarker ) erkennen kann, wann so etwas benötigt wird.

Es gibt zwei andere Möglichkeiten, wie ich das handhaben könnte:

  1. Erstellen Sie alle der Funktionen, die ich als virtuelle Funktionen von Token bezeichnen möchte, und lassen Sie sie nur für jede Unterklasse mit Ausnahme des oder der benötigten Elemente leer um sie zu behandeln, oder ...

  2. Erstellen Sie eine virtuelle Funktion in Token , die den korrekt typisierten Zeiger auf this zurückgibt, wenn er in bestimmten Subtypen aufgerufen wird, und einen Nullzeiger, wenn er für etwas anderes aufgerufen wird. Grundsätzlich eine Erweiterung des virtuellen Funktionssystems, das ich dort bereits benutze.

Die zweite Methode scheint mir besser zu sein als die bestehende und die erste. Aber ich würde gerne die Ansichten anderer erfahrener C ++ - Entwickler darüber erfahren. Oder ob ich mir zuviel Sorgen wegen Nebensächlichkeiten mache. : -)

    
Head Geek 24.02.2009, 02:47
quelle

6 Antworten

20

# 1 verschmutzt den Klassennamespace und die vtable für Objekte, die sie nicht benötigen. Ok, wenn Sie eine Handvoll Methoden haben, die im Allgemeinen implementiert werden, aber einfach hässlich, wenn sie nur für eine einzelne abgeleitete Klasse benötigt werden.

# 2 ist nur dynamic_cast<> in einem gepunkteten Kleid und Lippenstift. Client-Code wird nicht einfacher und verwickelt die gesamte Hierarchie, sodass die Basis und jede abgeleitete Klasse halb jeder anderen abgeleiteten Klasse sein müssen.

Benutze einfach dynamic_cast<> . Dafür ist es da.

    
Shog9 24.02.2009, 03:00
quelle
4

Wenn Sie wissen, dass die Konvertierung nicht ungültig sein kann, verwenden Sie einfach static_cast.

    
Hans Passant 24.02.2009 03:49
quelle
4

Wenn Sie clever werden möchten, können Sie auch ein Double-Dispatch -Muster erstellen, das zwei Drittel des Besuchermuster .

  • Erstellen Sie eine Basis-Klasse TokenVisitor , die leere virtuelle visit(SpecificToken*) -Methoden enthält.
  • Fügen Sie Token eine einzelne virtuelle accept(TokenVisitor*) -Methode hinzu, die die korrekt typisierte Methode für den übergebenen TokenVisitor aufruft.
  • Ableiten von TokenVisitor für die verschiedenen Dinge, die Sie auf verschiedene Arten über alle Token tun müssen.

Für das vollständige Besuchermuster, das für Baumstrukturen nützlich ist, durchlaufen die standardmäßigen accept -Methoden die untergeordneten Elemente, die token->accept(this); für jedes aufrufen.

    
Jeffrey Hantin 24.02.2009 03:37
quelle
2

Warum möchten Sie dynamic_cast vermeiden? Verursacht dies einen inakzeptablen Flaschenhals bei Ihrer Anwendung? Wenn nicht, ist es vielleicht nicht wert, etwas mit dem Code zu tun.

Wenn es Ihnen recht ist, ein wenig Sicherheit für ein bisschen Geschwindigkeit in Ihrer speziellen Situation einzutauschen, sollten Sie in der Lage sein, eine static_cast ; Das zementiert jedoch deine Annahme, dass du den Typ des Objekts kennst und es keine Chance gibt, dass die Besetzung schlecht ist. Wenn Ihre Annahme später falsch wird, könnten Sie in Ihrem Code einige mysteriöse Absturzfehler haben. Zurück zu meiner ursprünglichen Frage, bist du dir wirklich sicher, dass sich der Trade hier lohnt?

Wie für die Optionen, die Sie aufgelistet haben: Das erste klingt nicht wirklich nach einer Lösung, sondern nach einem Last-Minute-Hack, den ich erwarten würde, wenn jemand um 3 Uhr morgens Code schreibt. Die Funktionalität, die sich in Richtung der Basis der Klassenhierarchie schlängelt, ist einer der häufigsten Anti-Pattern-Treffer von Leuten, die neu in OOP sind. Tu es nicht.

Bei der zweiten Option, die Sie aufgelistet haben, wird dynamic_cast wirklich neu implementiert - wenn Sie auf einer Plattform mit nur Mistkompilern sind (ich habe gehört, dass der Compiler des Gamecube ein Viertel des verfügbaren Systems belegt) RAM mit RTTI info) das könnte sich lohnen, aber mehr als wahrscheinlich verschwenden Sie nur Ihre Zeit. Bist du dir wirklich sicher, dass es sich hier um etwas handelt, über das du dich besinnen solltest?

    
Andrew Khosravian 24.02.2009 04:42
quelle
1

Die wirklich saubere Möglichkeit, dynamic_cast zu verhindern, besteht darin, Zeiger des richtigen Typs an der richtigen Stelle zu haben. Abstraktion sollte sich um den Rest kümmern.

IMHO, der Grund dafür, dass dynamic_cast diese Reputation hat, ist, dass seine Leistung jedes Mal ein wenig abnimmt, wenn Sie einen weiteren Untertyp in Ihrer Klassenhierarchie hinzufügen. Wenn Sie 4-5 Klassen in der Hierarchie haben, brauchen Sie sich keine Sorgen zu machen.

    
jturcotte 24.02.2009 14:45
quelle
1

Lustige Sachen. Die dynamic_cast im Tokenizer impliziert, dass Sie Token auf der Grundlage der aktuellen Position im Textstream und der Logik in Token in Token teilen möchten. Jeder Token ist entweder eigenständig oder muss wie oben beschrieben einen Token erstellen, um Text korrekt zu verarbeiten.

Auf diese Weise ziehen Sie das generische Zeug heraus und können immer noch basierend auf der Hierarchie der Token analysieren. Sie können diesen ganzen Parser sogar datengesteuert machen, ohne dynamic_cast zu benutzen.

    
MSN 24.02.2009 14:59
quelle

Tags und Links