Compile-time vs run-time-Polymorphie in C++ - Vorteile/Nachteile
In C++, wenn es möglich ist, implementieren die gleiche Funktionalität, die entweder mit Laufzeit (sub-Klassen, virtuelle Funktionen) oder compile-Zeit (templates, überladen von Funktionen) Polymorphismus, warum würden Sie wählen einen über den anderen?
Ich würde denken, dass der kompilierte code größer wäre für compile-Zeit-Polymorphismus (mehr Methode/Klasse erstellten Definitionen für template-Typen), und das compile-Zeit würde Ihnen mehr Flexibilität, während der Laufzeit geben Sie "sicherer" Polymorphismus (d.h. schwieriger zu werden falsch verwendet, die durch Unfall).
Sind meine Annahmen korrekt? Gibt es irgendwelche anderen vor - /Nachteile? Kann mir jemand ein konkretes Beispiel nennen, wo beides wären gangbare Optionen doch der ein oder andere wäre eine deutlich bessere Wahl?
Auch, compile-Zeit-Polymorphismus produzieren, schneller code, da ist es nicht notwendig, rufen Funktionen über die vtable, oder wird dadurch optimiert werden, entfernt der compiler sowieso?
Beispiel:
class Base
{
virtual void print() = 0;
}
class Derived1 : Base
{
virtual void print()
{
//do something different
}
}
class Derived2 : Base
{
virtual void print()
{
//do something different
}
}
//Run time
void print(Base o)
{
o.print();
}
//Compile time
template<typename T>
print(T o)
{
o.print();
}
Die "Laufzeit" - Beispiel wird nicht kompilieren - kann man pass Basis-Klassen-Zeiger und erreichen dynamic dispatch, aber da Base ist Abstrakt, Sie kann nicht ein Objekt machen.
Mögliche Duplikate von C++ - templates für performance?
InformationsquelleAutor mclaassen | 2013-06-01
Du musst angemeldet sein, um einen Kommentar abzugeben.
Statischen Polymorphismus erzeugt schnelleren code, vor allem wegen der Möglichkeit, aggressive inlining. Virtuelle Funktionen können selten sein, eingebettet und zumeist in ein "nicht-polymorph" - Szenarien. Siehe diesen Artikel in C++ - FAQ. Wenn die Geschwindigkeit ist Ihr Ziel, Sie haben im Grunde keine Wahl.
Auf der anderen Seite, nicht nur die compile-Zeiten, sondern auch die Lesbarkeit und debuggability der code ist viel schlimmer, wenn die Verwendung statischer Polymorphismus. Zum Beispiel: abstrakte Methoden sind eine saubere Art und Weise der Durchsetzung der Implementierung bestimmter Methoden der Schnittstelle. Zu erreichen das gleiche Ziel mit statischer Polymorphismus, müssen Sie eine Wiederherstellung auf Konzept-Prüfung oder den neugierig recurring template pattern.
Nur die situation, wenn Sie wirklich für die dynamische Polymorphie ist, wenn die Implementierung nicht verfügbar ist zur compile-Zeit, zum Beispiel, wenn es geladen ist, aus einer dynamischen Bibliothek. In der Praxis, obwohl, möchten Sie möglicherweise die exchange-Leistung für saubereren code und schneller zu kompilieren.
InformationsquelleAutor maciek gajewski
Nachdem Sie filtern offensichtlich schlecht und suboptimale Fälle, ich glaube, du bist Links mit fast nichts. IMO ist es ziemlich selten, wenn Sie mit Blick auf diese Art der Wahl. Sie verbessern könnten, die Frage durch die Angabe eines Beispiels, und für die, die einen echten Vergleich van zur Verfügung gestellt werden.
Davon haben wir, die realistische Wahl, ich würde gehen für die Kompilierung Lösung-warum verschwenden Laufzeit für etwas, was nicht absolut notwendig ist? Ist auch etwas beschlossen wird zur compile-Zeit ist es einfacher zu denken, die Folgen im Kopf und Auswertung.
Virtuelle Funktionen, wie Funktionszeiger machen Sie nicht in der Lage zu erstellen genaue call-Graphen. Überprüfen Sie den unteren Rand, aber nicht so leicht von der Spitze. virtuelle Funktionen müssen Folgen einige Regeln, aber wenn Sie nicht, Sie haben sich alle von Ihnen für die Sünder.
Da gibt es auch einige Verluste auf die Leistung, wohl kein großer deal in der Mehrheit der Fälle, aber wenn kein Guthaben auf der anderen Seite, warum nehmen Sie es?
InformationsquelleAutor Balog Pal
Oft ja - durch mehrere Instanzen für verschiedene Kombinationen von template-Parametern, aber Bedenken Sie:
T mydata[12];
zugeteilt zu werden mit dem Objekt, die automatische Speicher für die lokalen Variablen etc., in der Erwägung, dass eine Laufzeit polymorph Umsetzung müssen möglicherweise verwenden Sie die dynamische Zuweisung (d.h.new[]
) - dies kann dramatische Auswirkungen auf die cache-Effizienz in einigen FällenVorlagen sicherlich tun:
angesichts der gleichen Vorlage instanziiert, die für unterschiedliche Arten, den gleichen code kann unterschiedliche Dinge bedeuten: zum Beispiel
T::f(1)
nennen könntevoid f(int) noexcept
Funktion in eine Instanzierung, einvirtual void f(double)
in einem anderen, einemT::f
functor-Objektoperator()(float)
in noch einem anderen; wenn man es aus einer anderen Perspektive, die unterschiedlichen parameter Typen können bieten, was der vorgefertigte code muss in welcher Weise auch immer Ihnen am besten passt,SFINAE können Sie Ihren code anpassen, zur compile-Zeit, um die Nutzung der effizientesten Schnittstellen-Objekte unterstützt, ohne die Objekte aktiv mit, um eine Empfehlung
durch das instanziieren-only-Funktionen genannte Aspekt oben erwähnt, können Sie "get away" mit der Instanziierung einer Vorlage-Klasse mit einem Typ, für den nur einige der Klasse template Funktionen zusammenstellen würde: in gewisser Weise ist das schlecht, weil die Programmierer erwarten können, dass Ihre scheinbar arbeiten
Template<MyType>
unterstützt alle Operationen, die derTemplate<>
unterstützt für andere Arten, nur um Sie scheitern, wenn Sie versuchen, eine bestimmte operation; in anderer Hinsicht ist es gut, weil Sie immer noch mitTemplate<>
wenn Sie nicht daran interessiert, alle OperationenTemplate<MyType>::operationX
gebrochen, und in der Regel geben einfacher Fehler Nachrichten, die früher in der compile -Wohl, da Sie etwas steifer angesichts der Vorlage Flexibilität vor. Die wichtigsten "Sicherheit" Probleme mit Laufzeit-Polymorphismus sind:
einige Probleme am Ende, die "Fett" - Schnittstellen (im Sinne Stroustrup erwähnt in Der Programmiersprache C++): APIs, mit Funktionen, die nur für einige der abgeleiteten Datentypen und algorithmischen code braucht, um "zu Fragen," die abgeleitete Typen "soll ich dies für Sie tun", "können Sie dies tun", "Tat, Arbeit" etc..
müssen Sie virtuelle Destruktoren: einige Klassen, die diese nicht haben (z.B.
std::vector
) - wodurch es schwieriger wird, die von Ihnen abgeleitet werden sicher, und das in-Objekt-Zeiger auf virtual dispatch-Tabellen sind nicht gültig über Prozesse, so dass es schwer zu legen runtime-polymorphe Objekte im shared memory für den Zugriff durch mehrere ProzesseSicher. Sagen Sie, Sie schreiben einen quick-sort-Funktion: Sie können nur unterstützen Datentypen, die sich aus einigen Sortierbar Basisklasse mit einer virtuellen Vergleich-Funktion und eine virtuelle swap-Funktion, oder Sie schreiben, könnte eine Art Vorlage mit einer
Less
Politik der parameter standardmäßig aufstd::less<T>
, undstd::swap<>
. Angesichts der Leistung von einer Art ist überwiegend dominiert von der Leistung dieser Vergleich-und swap-Operationen, eine Vorlage ist Massiv besser geeignet. Das ist der Grund, warum C++std::sort
deutlich übertrifft die C-Bibliothek den generischenqsort
- Funktion, die verwendet Funktionszeiger für das, was effektiv eine C-Implementierung des virtuellen Versand. Sehen hier für mehr darüber.Es ist auch oft sehr schneller, aber sehr gelegentlich die Summe der Auswirkungen der template-code aufzublähen möglicherweise überfordern die unzähligen Möglichkeiten compile-Zeit-Polymorphismus ist normalerweise schneller, so dass per Saldo ist es noch schlimmer.
InformationsquelleAutor Tony Delroy