Le compilateur Microsoft vous permet d'ajouter l'extension "novtable" à l'attribut "__declspec" lors de la déclaration d'une classe.
L'objectif affiché est de réduire considérablement la taille du code généré. Dans les expériences avec nos composants, la diminution était de 0,6 à 1,2% de la taille de la DLL.
Applicabilité: classes non destinées à instancier directement à partir d'eux.
Par exemple: des classes purement d'interface.
En code, cela ressemble à ceci:
struct __declspec(novtable) IDrawable { virtual void Draw() const = 0; };
Remarque: le mot clé struct a été utilisé pour déclarer une classe d'interface afin de débarrasser l'exemple des détails d'article non pertinents; alors que dans le cas de l'utilisation d'une classe, il faudrait utiliser public pour indiquer la "publicité" des méthodes. Pour la même raison, je n'ajouterai pas de destructeur virtuel à la classe d'interface dans cet article.Le nom "novtable" promet qu'il n'y aura pas de table virtuelle ... Mais comment fonctionne le mécanisme d'appel des fonctions virtuelles dans le code suivant:
Rappelez ce qui est ajouté lors de la déclaration d'une fonction virtuelle dans une classe:
- Définition d'une table de fonctions virtuelles. Une instance de cette table est utilisée pour toutes les instances de la classe.
- Un pointeur vers la table de fonctions virtuelles est ajouté aux membres de données de classe.
- Le code pour initialiser ce pointeur dans le constructeur de la classe.
Ainsi, dans notre exemple, il y aura une déclaration de deux tables de fonctions virtuelles: pour IDrawable et pour Rectangle. Lors de la création d'un objet Rectangle, le constructeur IDrawable est le premier à exécuter, ce qui initialise un pointeur sur sa table de fonctions virtuelles. Schématiquement, cela ressemble à ceci:

Puisque la fonction draw dans IDrawable est déclarée purement virtuelle ("= 0" est indiqué à la place du corps de la fonction), l'adresse de la fonction purecall générée par le compilateur est écrite dans le tableau des fonctions virtuelles.
Ensuite, le constructeur Rectangle est exécuté, ce qui initialise le même pointeur, mais à sa table de fonction virtuelle:

Que fait «novtable» et pourquoi Microsoft promet-il de réduire la taille du code?
C'est la définition inutile de la table de fonction virtuelle IDrawable et l'initialisation du pointeur vers celle-ci dans le constructeur IDrawable qui sont exclues du code résultant lors de l'ajout de «novtable».
Dans ce cas, lors de la construction d'un IDrawable, le pointeur vers la table des fonctions virtuelles contiendra une valeur imprévisible. Mais cela ne devrait pas nous déranger, car la création d'une implémentation avec accès aux fonctions virtuelles avant la construction complète de l'objet est généralement une erreur. Si, par exemple, une fonction non virtuelle de cette classe est appelée dans le constructeur de la classe de base, qui à son tour appelle une fonction virtuelle, alors la fonction purecall sera appelée sans novtable, et un comportement imprévisible avec novtable; aucune des options n'est acceptable.
Notez qu'il y a non seulement une diminution de la taille, mais aussi une certaine accélération du programme.
RTTI
Comme vous le savez, std :: dynamic_cast vous permet de convertir des pointeurs et des liens d'une instance d'une classe vers un pointeur et un lien vers une autre si ces classes sont liées hiérarchiquement et polymorphes (contiennent un tableau de fonctions virtuelles). À son tour, l'opérateur typeid vous permet d'obtenir des informations sur un objet lors de l'exécution en utilisant le pointeur (lien) vers cet objet qui lui est transmis. Ces capacités sont fournies par le mécanisme RTTI, qui utilise des informations de type situées en référence à la table virtuelle de la classe. Les détails de la structure et de l'emplacement dépendent du compilateur. Dans le cas du compilateur Microsoft, cela ressemble à ceci:

Par conséquent, si pendant la compilation le compilateur reçoit l'ordre d'activer RTTI, novtable exclut également la création d'une définition type_info pour IDrawable et les données de service requises pour cela.
Notez que si vous fournissez en quelque sorte la connaissance qu'un pointeur référencé (lien) vers une classe de base indique une implémentation d'un dérivé, alors std :: static_cast est plus efficace et ne nécessite pas RTTI.
Spécifique à Microsoft
En plus de MSVC, cette fonctionnalité avec la même syntaxe est présente dans Clang lors de la compilation sous Windows.
Conclusions
- __declspec (novtable) - n'affecte pas la quantité de mémoire occupée par les instances de classe.
- La réduction de la taille et l'accélération du programme sont assurées en éliminant la définition des tables de fonctions virtuelles inutilisées, la surcharge RTTI et en éliminant le code d'initialisation du pointeur vers la table de fonctions virtuelles dans les constructeurs de classe d'interface.