O compilador da Microsoft permite adicionar a extensão "novtable" ao atributo "__declspec" ao declarar uma classe.
O objetivo declarado é reduzir significativamente o tamanho do código gerado. Em experimentos com nossos componentes, a redução foi de 0,6 a 1,2% do tamanho da DLL.
Aplicabilidade: classes que não pretendem instanciar diretamente delas.
Por exemplo: puramente interfaces de classes.
No código, fica assim:
struct __declspec(novtable) IDrawable { virtual void Draw() const = 0; };
Nota: a palavra-chave struct foi usada para declarar uma classe de interface para livrar o exemplo de detalhes irrelevantes do artigo; enquanto que, no caso de usar uma classe, seria necessário usar public para indicar a "publicidade" dos métodos. Pelo mesmo motivo, não adicionarei um destruidor virtual à classe de interface neste artigo.O nome "novtable" promete que não haverá tabela virtual ... Mas como o mecanismo de chamada de funções virtuais funciona no seguinte código:
Lembre-se do que é adicionado ao declarar uma função virtual em uma classe:
- Definindo uma tabela de funções virtuais. Uma instância desta tabela é usada para todas as instâncias da classe.
- Um ponteiro para a tabela de função virtual é adicionado aos membros de dados da classe.
- O código para inicializar esse ponteiro no construtor da classe.
Assim, em nosso exemplo, haverá uma declaração de duas tabelas de funções virtuais: para IDrawable e para Rectangle. Ao criar um objeto Rectangle, o construtor IDrawable é o primeiro a ser executado, que inicializa um ponteiro para sua tabela de funções virtuais. Esquematicamente, fica assim:

Como a função draw em IDrawable é declarada puramente virtual ("= 0" é indicado em vez do corpo da função), o endereço da função purecall gerada pelo compilador é gravado na tabela de funções virtuais.
Em seguida, o construtor Rectangle é executado, o qual inicializa o mesmo ponteiro, mas em sua tabela de funções virtuais:

O que o "novtable" faz e por que a Microsoft promete reduzir o tamanho do código?
É a definição desnecessária da tabela de funções virtuais IDrawable e a inicialização do ponteiro para ela no construtor IDrawable que são excluídas do código resultante ao adicionar “novtable”.
Nesse caso, ao construir um IDrawable, o ponteiro para a tabela de funções virtuais conterá um valor imprevisível. Mas isso não deve nos incomodar, pois criar uma implementação com acesso a funções virtuais antes da construção completa do objeto geralmente é um erro. Se, por exemplo, uma função não virtual dessa classe for chamada no construtor da classe base, que por sua vez chama uma função virtual, a função purecall será chamada sem novtable e comportamento imprevisível com novtable; nenhuma das opções pode ser aceitável.
Observe que não há apenas uma diminuição no tamanho, mas também alguma aceleração do programa.
RTTI
Como você sabe, std :: dynamic_cast permite converter ponteiros e links de uma instância de uma classe para um ponteiro e um link para outro, se essas classes forem hierarquicamente relacionadas e polimórficas (contêm uma tabela de funções virtuais). Por sua vez, o operador typeid permite obter informações sobre um objeto em tempo de execução, usando o ponteiro (link) para esse objeto passado para ele. Esses recursos são fornecidos pelo mecanismo RTTI, que usa informações de tipo localizadas com referência à tabela de tabelas da classe. Os detalhes da estrutura e localização dependem do compilador. No caso do compilador da Microsoft, fica assim:

Portanto, se durante a compilação o ordenador for solicitado a habilitar o RTTI, o novtable também excluirá a criação de uma definição type_info para IDrawable e os dados de serviço necessários para isso.
Observe que, se você fornecer de alguma forma o conhecimento de que um ponteiro redutível (link) para a classe base indica a implementação da derivada, std :: static_cast é mais eficiente e não requer RTTI.
Específico da Microsoft
Além do MSVC, esse recurso com a mesma sintaxe está presente no Clang ao compilar no Windows.
Conclusões
- __declspec (novtable) - não afeta a quantidade de memória ocupada por instâncias de classe.
- É garantido reduzir o tamanho e acelerar o programa, eliminando a definição de tabelas de funções virtuais não utilizadas, sobrecarga de RTTI e excluindo o código de inicialização do ponteiro para a tabela de funções virtuais nos construtores de classe de interface.