Wann sollten virtuelle Destruktoren NICHT verwendet werden?


48

Ich glaubte, ich habe viele Male nach virtuellen Destruktoren gesucht, die meisten erwähnen den Zweck von virtuellen Destruktoren und warum Sie virtuelle Destruktoren benötigen. Außerdem denke ich, dass Destruktoren in den meisten Fällen virtuell sein müssen.

Dann lautet die Frage: Warum setzt c ++ nicht standardmäßig alle Destruktoren virtuell? oder bei anderen fragen:

Wann brauche ich keine virtuellen Destruktoren?

In welchem ​​Fall sollte ich keine virtuellen Destruktoren verwenden?

Was kostet die Verwendung virtueller Destruktoren, wenn ich sie verwende, auch wenn sie nicht benötigt werden?


6
Und wenn Ihre Klasse nicht geerbt werden soll? Schauen Sie sich viele der Standardbibliotheksklassen an. Nur wenige verfügen über virtuelle Funktionen, da sie nicht zur Vererbung vorgesehen sind.
Einige Programmierer Kerl

4
Außerdem denke ich, dass Destruktoren in den meisten Fällen virtuell sein müssen. Nee. Überhaupt nicht. Das glauben nur diejenigen, die die Erbschaft missbrauchen (anstatt die Zusammensetzung zu bevorzugen). Ich habe ganze Anwendungen mit nur einer Handvoll Basisklassen und virtuellen Funktionen gesehen.
Matthieu M.

1
@underscore_d Bei typischen Implementierungen würde für jede polymorphe Klasse zusätzlicher Code generiert, es sei denn, all diese impliziten Elemente wurden entfernt und optimiert. Innerhalb der gängigen ABIs handelt es sich dabei um mindestens eine V-Tabelle für jede Klasse. Das Layout der Klasse muss ebenfalls geändert werden. Sie können nicht zuverlässig zurück sein, wenn Sie eine solche Klasse als Teil einer öffentlichen Schnittstelle veröffentlicht haben, da eine erneute Änderung die ABI-Kompatibilität beeinträchtigen würde, da es offensichtlich (wenn überhaupt möglich) schlecht ist, mit einer Devirtualisierung als Schnittstellenvertrag im Allgemeinen zu rechnen.
FrankHB

1
@underscore_d Der Ausdruck "zur Kompilierungszeit" ist ungenau, aber ich denke, dies bedeutet, dass ein virtueller Destruktor weder trivial noch mit constexpr angegeben werden kann, sodass die zusätzliche Codegenerierung schwer zu vermeiden ist (es sei denn, Sie vermeiden die Zerstörung solcher Objekte vollständig) Dies würde die Laufzeitleistung mehr oder weniger beeinträchtigen.
FrankHB

2
@underscore_d Der "Zeiger" scheint roter Hering zu sein. Möglicherweise sollte es ein Zeiger auf member sein (was per Definition kein Zeiger ist). Bei gewöhnlichen ABIs passt ein Zeiger auf ein Element häufig nicht in ein Maschinenwort (als typische Zeiger), und das Ändern einer Klasse von nicht polymorph zu polymorph ändert häufig die Größe des Zeigers auf ein Element dieser Klasse.
FrankHB

Antworten:


41

Wenn Sie einer Klasse einen virtuellen Destruktor hinzufügen:

  • In den meisten (allen?) aktuellen C ++ - Implementierungen muss jede Objektinstanz dieser Klasse einen Zeiger auf die virtuelle Versandtabelle für den Laufzeittyp speichern und diese virtuelle Versandtabelle selbst dem ausführbaren Image hinzufügen

  • Die Adresse der virtuellen Verteilertabelle ist nicht unbedingt prozessübergreifend gültig. Dies kann die sichere Freigabe solcher Objekte im gemeinsam genutzten Speicher verhindern

  • Ein eingebetteter virtueller Zeiger frustriert das Erstellen einer Klasse mit einem Speicherlayout, das einem bekannten Eingabe- oder Ausgabeformat entspricht (z. B. Price_Tick*könnte ein Speicher direkt auf einen geeignet ausgerichteten Speicher in einem eingehenden UDP-Paket ausgerichtet und zum Parsen / Zugreifen oder Ändern der Daten verwendet werden) Platzieren einer newsolchen Klasse, um Daten in ein ausgehendes Paket zu schreiben)

  • Die Destruktor-Aufrufe selbst müssen möglicherweise - unter bestimmten Umständen - virtuell und daher offline gesendet werden, wohingegen nicht-virtuelle Destruktoren möglicherweise inline geschaltet oder optimiert werden, wenn dies für den Aufrufer trivial oder irrelevant ist

Das Argument "nicht entworfen, um von geerbt zu werden" wäre kein praktischer Grund dafür, nicht immer einen virtuellen Destruktor zu haben, wenn es nicht auch in praktischer Weise schlechter wäre, wie oben erläutert; Angesichts der Tatsache, dass es schlimmer ist, ist dies ein wichtiges Kriterium für die Kostenübernahme: Standardmäßig wird ein virtueller Destruktor verwendet, wenn Ihre Klasse als Basisklasse verwendet werden soll . Dies ist nicht immer erforderlich, stellt jedoch sicher, dass die Klassen in der Hierarchie ohne unbeabsichtigtes undefiniertes Verhalten freier verwendet werden können, wenn ein abgeleiteter Klassendestruktor mithilfe eines Basisklassenzeigers oder einer Referenz aufgerufen wird.

"In den meisten Fällen müssen Destruktoren virtuell sein"

Nicht so ... viele Klassen haben keinen solchen Bedarf. Es gibt so viele Beispiele, bei denen es unnötig ist, sie aufzuzählen. Schauen Sie sich einfach Ihre Standardbibliothek an oder sagen Sie boost, und Sie werden feststellen, dass es eine große Mehrheit von Klassen gibt, die keine virtuellen Destruktoren haben. In Boost 1.53 zähle ich 72 von 494 virtuellen Destruktoren.


23

In welchem ​​Fall sollte ich keine virtuellen Destruktoren verwenden?

  1. Für eine konkrete Klasse, die nicht vererbt werden will.
  2. Für eine Basisklasse ohne polymorphe Löschung. Beide Clients sollten nicht in der Lage sein, mit einem Zeiger auf Base polymorph zu löschen.

Übrigens,

In welchem ​​Fall sollten virtuelle Destruktoren verwendet werden?

Für eine Basisklasse mit polymorpher Löschung.


7
+1 für # 2, speziell ohne polymorphe Deletion . Wenn Ihr Destruktor niemals über einen Basiszeiger aufgerufen werden kann, ist es unnötig und überflüssig, ihn virtuell zu machen, insbesondere wenn Ihre Klasse zuvor nicht virtuell war (sodass sie mit RTTI neu aufgebläht wird). Um zu verhindern, dass ein Benutzer dies verletzt, sollten Sie den dtor der Basisklasse, wie von Herb Sutter empfohlen, als geschützt und nicht virtuell definieren, sodass er nur von / nach einem abgeleiteten Destruktor aufgerufen werden kann.
Underscore_d

@underscore_d imho, dass ein wichtiger Punkt, den ich in den Antworten verpasst habe, da bei Vorhandensein von Vererbung der einzige Fall ist, in dem ich keinen virtuellen Konstruktor benötige, wenn ich sicherstellen kann, dass er nie benötigt wird
ehemals

14

Was kostet die Verwendung virtueller Destruktoren, wenn ich sie verwende, auch wenn sie nicht benötigt werden?

Die Kosten für die Einführung einer virtuellen Funktion in eine Klasse (geerbt oder Teil der Klassendefinition) sind möglicherweise sehr hoch (oder nicht abhängig vom Objekt), wenn ein virtueller Zeiger pro Objekt gespeichert wird.

struct Integer
{
    virtual ~Integer() {}
    int value;
};

In diesem Fall sind die Speicherkosten relativ groß. Die tatsächliche Speichergröße einer Klasseninstanz sieht auf 64-Bit-Architekturen jetzt häufig folgendermaßen aus:

struct Integer
{
    // 8 byte vptr overhead
    int value; // 4 bytes
    // typically 4 more bytes of padding for alignment of vptr
};

Die Gesamtsumme für diese IntegerKlasse beträgt 16 Bytes im Gegensatz zu nur 4 Bytes. Wenn wir eine Million davon in einem Array speichern, werden 16 Megabyte Arbeitsspeicher verbraucht: die doppelte Größe des typischen 8 MB L3-CPU-Caches, und das wiederholte Durchlaufen eines solchen Arrays kann um ein Vielfaches langsamer sein als das 4-Megabyte-Äquivalent ohne den virtuellen Zeiger infolge zusätzlicher Cache-Fehlschläge und Seitenfehler.

Diese Kosten für virtuelle Zeiger pro Objekt steigen jedoch nicht mit mehr virtuellen Funktionen. Sie können 100 virtuelle Memberfunktionen in einer Klasse haben, und der Overhead pro Instanz wäre immer noch ein einzelner virtueller Zeiger.

Der virtuelle Zeiger ist vom Overhead-Standpunkt aus in der Regel das unmittelbarere Problem. Zusätzlich zu einem virtuellen Zeiger pro Instanz fallen jedoch Kosten pro Klasse an. Jede Klasse mit virtuellen Funktionen generiert einen vtableIn-Memory-Speicher, in dem Adressen zu den Funktionen gespeichert sind, die sie bei einem virtuellen Funktionsaufruf tatsächlich aufrufen soll (virtueller / dynamischer Versand). Die vptrpro Instanz gespeicherte zeigt dann auf diese klassenspezifische vtable. Dieser Overhead ist normalerweise weniger bedenklich, aber er kann Ihre Binärgröße vtableerhöhen und ein wenig Laufzeitkosten verursachen, wenn dieser Overhead für tausend Klassen in einer komplexen Codebasis unnötig gezahlt wird, z mehr virtuelle funktionen im mix.

Java-Entwickler, die in leistungskritischen Bereichen arbeiten, verstehen diese Art von Overhead sehr gut (obwohl dies häufig im Zusammenhang mit dem Boxen beschrieben wird), da ein benutzerdefinierter Java-Typ implizit von einer zentralen objectBasisklasse erbt und alle Funktionen in Java implizit virtuell (überschreibbar) sind ) in der Natur, sofern nicht anders angegeben. Infolgedessen benötigt ein Java Integerauf 64-Bit-Plattformen in vptrder Regel 16 Byte Speicher aufgrund dieser pro Instanz zugeordneten Metadaten. In Java ist es normalerweise unmöglich, so etwas wie eine einzelne intin eine Klasse zu packen, ohne dafür eine Laufzeit zu bezahlen Leistungskosten dafür.

Dann lautet die Frage: Warum setzt c ++ nicht standardmäßig alle Destruktoren virtuell?

C ++ begünstigt die Leistung mit einer Art "Pay-as-you-go" -Mentalente und einer Menge von Bare-Metal-Hardware-basierten Designs, die von C geerbt wurden. Es soll nicht unnötig den Overhead beinhalten, der für die Erstellung von virtuellen Tabellen und den dynamischen Versand für erforderlich ist jede einzelne betroffene Klasse / Instanz. Wenn die Leistung nicht einer der Hauptgründe ist, warum Sie eine Sprache wie C ++ verwenden, können Sie möglicherweise mehr von anderen Programmiersprachen profitieren, da ein Großteil der C ++ - Sprache weniger sicher und schwieriger ist, als dies im Idealfall häufig der Fall ist der Hauptgrund für ein solches Design.

Wann brauche ich keine virtuellen Destruktoren?

Ziemlich oft. Wenn eine Klasse nicht für die Vererbung konzipiert ist, benötigt sie keinen virtuellen Destruktor und würde nur einen möglicherweise hohen Aufwand für etwas bezahlen, das sie nicht benötigt. Auch wenn eine Klasse so konzipiert ist, dass sie geerbt wird, Sie jedoch Subtyp-Instanzen niemals über einen Basiszeiger löschen, ist kein virtueller Destruktor erforderlich. In diesem Fall ist es eine sichere Praxis, einen geschützten nicht-virtuellen Destruktor wie folgt zu definieren:

class BaseClass
{
protected:
    // Disallow deleting/destroying subclass objects through `BaseClass*`.
    ~BaseClass() {}
};

In welchem ​​Fall sollte ich keine virtuellen Destruktoren verwenden?

Es ist tatsächlich leichte Abdeckung , wenn Sie sollten virtuelle Destruktoren verwenden. Sehr oft sind weit mehr Klassen in Ihrer Codebasis nicht für die Vererbung vorgesehen.

std::vectorZum Beispiel ist es nicht so konzipiert, dass es vererbt wird, und sollte normalerweise nicht vererbt werden (sehr wackeliges Design), da dies std::vectorzusätzlich zu Problemen beim Aufteilen von Objekten zu Problemen beim Löschen von Basiszeigern führt ( ein virtueller Destruktor wird absichtlich vermieden) Die abgeleitete Klasse fügt jeden neuen Status hinzu.

Im Allgemeinen sollte eine geerbte Klasse entweder einen öffentlichen virtuellen Destruktor oder einen geschützten, nicht virtuellen Destruktor haben. Ab C++ Coding StandardsKapitel 50:

50. Machen Sie Basisklassen-Destruktoren öffentlich und virtuell oder geschützt und nicht virtuell. Löschen oder nicht löschen; Das ist die Frage: Wenn das Löschen durch einen Zeiger auf eine Base erlaubt sein soll, muss der Destruktor der Base öffentlich und virtuell sein. Ansonsten sollte es geschützt und nicht virtuell sein.

Eines der Dinge, die C ++ implizit hervorhebt (weil Entwürfe dazu neigen, sehr spröde und umständlich zu werden und möglicherweise auch sonst unsicher zu werden), ist die Idee, dass Vererbung kein Mechanismus ist, der als nachträglicher Gedanke gedacht ist. Es ist ein Erweiterungsmechanismus mit Polymorphismus im Auge, der jedoch Voraussicht erfordert, wo Erweiterbarkeit erforderlich ist. Infolgedessen sollten Ihre Basisklassen im Voraus als Wurzeln einer Vererbungshierarchie entworfen werden und nicht als Erbe von später als nachträglicher Einfall ohne eine solche Vorausschau.

In den Fällen, in denen Sie lediglich den vorhandenen Code übernehmen möchten, wird die Komposition häufig stark empfohlen (Prinzip der kombinierten Wiederverwendung).


9

Warum setzt c ++ nicht standardmäßig alle Destruktoren auf virtuell? Kosten für zusätzlichen Speicher und Aufruf der virtuellen Methodentabelle. C ++ wird für System-RT-Programmierung mit geringer Latenz verwendet, wo dies eine Belastung darstellen könnte.


Destruktoren sollten in harten Echtzeitsystemen nicht in erster Linie verwendet werden, da viele Ressourcen wie dynamischer Speicher nicht verwendet werden können, um starke Fristgarantien zu bieten
Marco A.,

9
@MarcoA. Seit wann implizieren Destruktoren eine dynamische Speicherzuweisung?
chbaker0

@ chbaker0 Ich habe ein "Gefällt mir" verwendet. Sie werden meiner Erfahrung nach einfach nicht verwendet.
Marco A.

6
Es ist auch Unsinn, dass dynamischer Speicher in harten Echtzeitsystemen nicht verwendet werden kann. Es ist ziemlich trivial zu beweisen, dass ein vorkonfigurierter Heap mit festen Zuordnungsgrößen und einer Zuordnungsbitmap entweder Speicher zuweist oder in der Zeit, die zum Scannen dieser Bitmap benötigt wird, einen Zustand außerhalb des Arbeitsspeichers zurückgibt.
MSalters

@msalters das bringt mich zum Nachdenken: Stellen Sie sich ein Programm vor, in dem die Kosten jeder Operation im Typensystem gespeichert wurden. Ermöglichen der Überprüfung von Echtzeitgarantien während der Kompilierung.
Yakk

5

Dies ist ein gutes Beispiel dafür, wann der virtuelle Destruktor nicht verwendet werden sollte: Von Scott Meyers:

Wenn eine Klasse keine virtuellen Funktionen enthält, ist dies oft ein Hinweis darauf, dass sie nicht als Basisklasse verwendet werden soll. Wenn eine Klasse nicht als Basisklasse verwendet werden soll, ist es normalerweise eine schlechte Idee, den Destruktor virtuell zu machen. Betrachten Sie dieses Beispiel anhand einer Diskussion im ARM:

// class for representing 2D points
class Point {
public:
    Point(short int xCoord, short int yCoord);
    ~Point();
private:
    short int x, y;
};

Wenn ein kurzes int 16 Bit belegt, kann ein Point-Objekt in ein 32-Bit-Register passen. Darüber hinaus kann ein Point-Objekt als 32-Bit-Menge an Funktionen übergeben werden, die in anderen Sprachen wie C oder FORTRAN geschrieben wurden. Wenn der Destruktor von Point jedoch virtuell gemacht wird, ändert sich die Situation.

In dem Moment, in dem Sie ein virtuelles Mitglied hinzufügen, wird ein virtueller Zeiger zu Ihrer Klasse hinzugefügt, der auf die virtuelle Tabelle für diese Klasse zeigt.


If a class does not contain any virtual functions, that is often an indication that it is not meant to be used as a base class.Wut. Erinnert sich noch jemand an die guten alten Zeiten, in denen wir mithilfe von Klassen und Vererbung aufeinanderfolgende Ebenen wiederverwendbarer Elemente und Verhaltensweisen aufbauen durften, ohne uns um virtuelle Methoden kümmern zu müssen? Komm schon, Scott. Ich verstehe den Kernpunkt, aber das "oft" ist wirklich zu erreichen.
Underscore_d

3

Ein virtueller Destruktor erhöht die Laufzeitkosten. Die Kosten sind besonders hoch, wenn die Klasse keine anderen virtuellen Methoden hat. Der virtuelle Destruktor wird auch nur in einem bestimmten Szenario benötigt, in dem ein Objekt durch einen Zeiger auf eine Basisklasse gelöscht oder auf andere Weise zerstört wird. In diesem Fall muss der Basisklassendestruktor virtuell sein, und der Destruktor einer abgeleiteten Klasse ist implizit virtuell. Es gibt einige Szenarien, in denen eine polymorphe Basisklasse so verwendet wird, dass der Destruktor nicht virtuell sein muss:

  • Wenn Instanzen abgeleiteter Klassen nicht auf dem Heap zugeordnet sind, z. B. nur direkt auf dem Stack oder in anderen Objekten. (Außer wenn Sie nicht initialisierten Speicher und Platzierungsoperator new verwenden.)
  • Wenn Instanzen von abgeleiteten Klassen auf dem Heap zugeordnet sind, das Löschen jedoch nur über Zeiger auf die am meisten abgeleitete Klasse erfolgt, z std::unique_ptr<Derived>. Ein weiteres Beispiel ist die Zuweisung von Objekten mit std::make_shared<Derived>(). Es ist in Ordnung zu verwenden, std::shared_ptr<Base>solange der anfängliche Zeiger a war std::shared_ptr<Derived>. Dies liegt daran, dass freigegebene Zeiger einen eigenen dynamischen Versand für Destruktoren (den Deleter) haben, der nicht unbedingt auf einem Destruktor für virtuelle Basisklassen beruht.

Natürlich kann jede Konvention, Objekte nur auf die oben genannten Arten zu verwenden, leicht gebrochen werden. Daher bleibt Herb Sutters Rat wie immer gültig: "Destruktoren der Basisklasse sollten entweder öffentlich und virtuell oder geschützt und nicht virtuell sein." Wenn jemand versucht, einen Zeiger auf eine Basisklasse mit nicht-virtuellem Destruktor zu löschen, wird er auf diese Weise höchstwahrscheinlich zur Kompilierungszeit einen Zugriffsverletzungsfehler erhalten.

Andererseits gibt es Klassen, die nicht als (öffentliche) Basisklassen konzipiert sind. Meine persönliche Empfehlung ist, sie finalin C ++ 11 oder höher zu erstellen. Wenn es sich um einen quadratischen Stift handelt, funktioniert es wahrscheinlich nicht so gut wie ein runder Stift. Dies hängt unter anderem mit meiner Präferenz für einen expliziten Vererbungsvertrag zwischen Basisklasse und abgeleiteter Klasse, für das NVI-Entwurfsmuster (non-virtual interface) für abstrakte statt für konkrete Basisklassen und meiner Abneigung gegen geschützte Mitgliedsvariablen zusammen , aber ich weiß, dass all diese Ansichten bis zu einem gewissen Grad umstritten sind.


1

Das Deklarieren eines Destruktors virtualist nur dann erforderlich, wenn Sie vorhaben, den Destruktor classvererbbar zu machen . In der Regel stellen die Klassen der Standardbibliothek (z. B. std::string) keinen virtuellen Destruktor zur Verfügung und sind daher nicht für die Unterklasse gedacht.


3
Der Grund ist Unterklassen + Verwendung von Polymorphismus. Ein virtueller Destruktor ist nur erforderlich, wenn eine dynamische Auflösung erforderlich ist, das heißt, ein Verweis / Zeiger / was auch immer auf die Master-Klasse verweist möglicherweise tatsächlich auf eine Instanz einer Unterklasse.
Michel Billaud

2
@MichelBillaud tatsächlich kann man immer noch Polymorphismus ohne virtuelle dtors haben. Ein virtueller dtor wird NUR zum polymorphen Löschen benötigt, dh zum Aufrufen deleteeines Zeigers auf eine Basisklasse.
chbaker0

1

Das Erstellen der vtable ist im Konstruktor mit einem Mehraufwand verbunden (wenn Sie keine anderen virtuellen Funktionen haben, sollten Sie in diesem Fall wahrscheinlich, aber nicht immer, auch einen virtuellen Destruktor haben). Und wenn Sie keine anderen virtuellen Funktionen haben, wird Ihr Objekt um eine Zeigergröße größer, als es sonst erforderlich ist. Offensichtlich kann die vergrößerte Größe große Auswirkungen auf kleine Objekte haben.

Es gibt einen zusätzlichen Speicher, der gelesen wird, um die Vtable abzurufen und dann die Funktion indirekt aufzurufen. Dies ist ein Overhead für den nicht-virtuellen Destruktor, wenn der Destruktor aufgerufen wird. Und natürlich wird in der Folge für jeden Aufruf des Destruktors ein wenig zusätzlicher Code generiert. Dies gilt für Fälle, in denen der Compiler den tatsächlichen Typ nicht ableiten kann. In den Fällen, in denen er den tatsächlichen Typ ableiten kann, verwendet der Compiler nicht die vtable, sondern ruft den Destruktor direkt auf.

Sie sollten einen virtuellen Destruktor haben, wenn Ihre Klasse als Basisklasse gedacht ist, insbesondere wenn sie von einer anderen Entität als dem Code erstellt / zerstört werden kann, der weiß, welchen Typ sie bei der Erstellung hat. Dann benötigen Sie einen virtuellen Destruktor.

Wenn Sie sich nicht sicher sind, verwenden Sie den virtuellen Destruktor. Es ist einfacher, Virtual zu entfernen, wenn es sich um ein Problem handelt, als zu versuchen, den Fehler zu finden, der durch "Der richtige Destruktor wird nicht aufgerufen" verursacht wird.

Kurz gesagt, Sie sollten keinen virtuellen Destruktor haben, wenn: 1. Sie keine virtuellen Funktionen haben. 2. Leiten Sie nicht von der Klasse ab (markieren Sie sie finalin C ++ 11, damit der Compiler erkennt, ob Sie versuchen, davon abzuleiten).

In den meisten Fällen wird für das Erstellen und Löschen eines Objekts nur dann viel Zeit aufgewendet, wenn "viel Inhalt" vorhanden ist (das Erstellen eines 1-MB-Strings wird offensichtlich einige Zeit in Anspruch nehmen, da mindestens 1 MB Daten erforderlich sind kopiert werden, wo immer es sich gerade befindet). Das Zerstören eines 1-MB-Strings ist nicht schlimmer als das Zerstören eines 150-MB-Strings. Beide erfordern das Freigeben des String-Speichers und nicht viel mehr. Daher ist die dort verbrachte Zeit in der Regel gleich [es sei denn, es handelt sich um ein Debug-Build, bei dem das Freigeben häufig den Speicher mit a füllt "Giftmuster" - aber so werden Sie Ihre reale Anwendung nicht in der Produktion ausführen.

Kurz gesagt, es gibt einen geringen Overhead, aber bei kleinen Objekten kann dies einen Unterschied machen.

Beachten Sie auch, dass Compiler in einigen Fällen die virtuelle Suche optimieren können, sodass dies nur eine Strafe ist

Wie immer, wenn es um Leistung, Speicherbedarf und Ähnliches geht: Vergleiche die Ergebnisse mit Alternativen, vergleiche sie mit Profilen und Messungen und schaue nach, wo die meiste Zeit / der meiste Speicher verbraucht wird, und versuche nicht, die 90% von zu optimieren Code, der nicht viel ausgeführt wird [die meisten Anwendungen haben ungefähr 10% Code, der einen großen Einfluss auf die Ausführungszeit hat, und 90% Code, der überhaupt keinen großen Einfluss hat]. Tun Sie dies in einer hohen Optimierungsstufe, damit Sie bereits den Vorteil haben, dass der Compiler gute Arbeit leistet! Und wiederholen, erneut prüfen und schrittweise verbessern. Versuchen Sie nicht, klug zu sein und herauszufinden, was wichtig ist und was nicht, es sei denn, Sie haben viel Erfahrung mit dieser Art von Anwendung.


1
"wird ein Overhead im Konstruktor für die Erstellung der vtable sein" - die vtable wird normalerweise pro Klasse vom Compiler "erstellt", wobei der Konstruktor nur den Overhead hat, einen Zeiger in der im Bau befindlichen Objektinstanz darauf zu speichern.
Tony

Außerdem ... geht es mir nur darum, eine vorzeitige Optimierung zu vermeiden, aber im Gegenzug You **should** have a virtual destructor if your class is intended as a base-classhandelt es sich um eine grobe Vereinfachung - und vorzeitige Pessimisierung . Dies ist nur erforderlich, wenn eine abgeleitete Klasse durch einen Zeiger auf base gelöscht werden darf. In vielen Situationen ist das nicht so. Wenn Sie wissen , es ist, dann sicher, entstehen den Overhead. Was übrigens immer hinzugefügt wird, auch wenn die tatsächlichen Aufrufe vom Compiler statisch aufgelöst werden können. Andernfalls lohnt es sich nicht, genau zu steuern, was Personen mit Ihren Objekten tun können
underscore_d
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.