Mit dem Microsoft-Compiler können Sie beim Deklarieren einer Klasse die Erweiterung "novtable" für das Attribut "__declspec" hinzufügen.
Das erklärte Ziel ist es, die Größe des generierten Codes erheblich zu reduzieren. In Experimenten mit unseren Komponenten betrug die Abnahme 0,6 bis 1,2 Prozent der Größe der DLL.
Anwendbarkeit: Klassen, die nicht direkt von ihnen instanziiert werden sollen.
Zum Beispiel: reine Schnittstellenklassen.
Im Code sieht es so aus:
struct __declspec(novtable) IDrawable { virtual void Draw() const = 0; };
Hinweis: Das Schlüsselwort struct wurde verwendet, um eine Schnittstellenklasse zu deklarieren, um das Beispiel irrelevanter Artikeldetails zu entfernen. Im Fall der Verwendung einer Klasse müsste man public verwenden, um die "Publizität" von Methoden anzuzeigen. Aus dem gleichen Grund werde ich der Schnittstellenklasse in diesem Artikel keinen virtuellen Destruktor hinzufügen.Der Name "novtable" verspricht, dass es keine virtuelle Tabelle geben wird ... Aber wie funktioniert der Mechanismus zum Aufrufen virtueller Funktionen im folgenden Code:
Erinnern Sie sich daran, was hinzugefügt wird, wenn eine virtuelle Funktion in einer Klasse deklariert wird:
- Definieren einer Tabelle mit virtuellen Funktionen. Eine Instanz dieser Tabelle wird für alle Instanzen der Klasse verwendet.
- Ein Zeiger auf die virtuelle Funktionstabelle wird den Klassendatenelementen hinzugefügt.
- Der Code zum Initialisieren dieses Zeigers im Konstruktor der Klasse.
In unserem Beispiel gibt es daher eine Deklaration von zwei virtuellen Funktionstabellen: für IDrawable und für Rectangle. Beim Erstellen eines Rechteckobjekts wird zuerst der IDrawable-Konstruktor ausgeführt, der einen Zeiger auf seine virtuelle Funktionstabelle initialisiert. Schematisch sieht es so aus:

Da die Zeichenfunktion in IDrawable als rein virtuell deklariert ist ("= 0" wird anstelle des Funktionskörpers angegeben), wird die Adresse der vom Compiler generierten Purecall-Funktion in die Tabelle der virtuellen Funktionen geschrieben.
Dann wird der Rechteckkonstruktor ausgeführt, der denselben Zeiger initialisiert, jedoch auf seine virtuelle Funktionstabelle:

Was macht "novtable" und warum verspricht Microsoft, die Größe des Codes zu reduzieren?
Es ist die unnötige Definition der virtuellen IDrawable-Funktionstabelle und die Initialisierung des Zeigers darauf im IDrawable-Konstruktor, die beim Hinzufügen von "novtable" aus dem resultierenden Code ausgeschlossen werden.
In diesem Fall enthält der Zeiger auf die virtuelle Funktionstabelle beim Erstellen eines IDrawable einen unvorhersehbaren Wert. Dies sollte uns jedoch nicht stören, da das Erstellen einer Implementierung mit Zugriff auf virtuelle Funktionen vor der vollständigen Erstellung des Objekts normalerweise ein Fehler ist. Wenn beispielsweise eine nicht virtuelle Funktion dieser Klasse im Konstruktor der Basisklasse aufgerufen wird, die wiederum eine virtuelle Funktion aufruft, wird die purecall-Funktion ohne novtable und unvorhersehbares Verhalten mit novtable aufgerufen. Keine der Optionen ist möglicherweise akzeptabel.
Beachten Sie, dass nicht nur die Größe abnimmt, sondern auch das Programm etwas beschleunigt wird.
RTTI
Wie Sie wissen, können Sie mit std :: dynamic_cast Zeiger und Links von einer Instanz einer Klasse zu einem Zeiger und einen Link zu einer anderen umwandeln, wenn diese Klassen hierarchisch verwandt und polymorph sind (eine Tabelle mit virtuellen Funktionen enthalten). Mit dem Operator typeid können Sie wiederum Informationen zur Laufzeit eines Objekts mithilfe des Zeigers (Links) auf das an ihn übergebene Objekt abrufen. Diese Funktionen werden vom RTTI-Mechanismus bereitgestellt, der Typinformationen verwendet, die sich auf die vtable der Klasse beziehen. Die Details der Struktur und des Speicherorts hängen vom Compiler ab. Im Fall des Microsoft-Compilers sieht es so aus:

Wenn der Compiler während der Kompilierung angewiesen wird, RTTI zu aktivieren, schließt novtable daher auch die Erstellung einer type_info-Definition für IDrawable und der dafür erforderlichen Servicedaten aus.
Beachten Sie, dass std :: static_cast effizienter ist und keine RTTI erfordert, wenn Sie das Wissen bereitstellen, dass ein reduzierbarer Zeiger (Link) auf die Basisklasse die Implementierung der Ableitung anzeigt.
Microsoft-spezifisch
Zusätzlich zu MSVC ist diese Funktion mit derselben Syntax in Clang beim Kompilieren unter Windows vorhanden.
Schlussfolgerungen
- __declspec (novtable) - hat keinen Einfluss auf die von Klasseninstanzen belegte Speicherkapazität.
- Das Reduzieren der Größe und das Beschleunigen des Programms wird sichergestellt, indem die Definition nicht verwendeter virtueller Funktionstabellen, der RTTI-Overhead und das Ausschließen des Initialisierungscodes für den Zeiger auf die virtuelle Funktionstabelle in den Schnittstellenklassenkonstruktoren eliminiert werden.