Ir para o conteúdo

Última atualização:2026-10-13

Funções virtuais em C++

Sobre o autor:

Conta pública pessoal no WeChat:

Funções virtuais em C++ | C++那些事

1. Funções virtuais e polimorfismo em tempo de execução

Código correspondente: emp.cpp

A chamada de uma função virtual depende do tipo do objeto apontado ou referenciado, e não do tipo do próprio ponteiro ou referência.

2. vptr e vtable

Veja vptr e vtable

3. Argumentos padrão em funções virtuais

Código correspondente: default_arg.cpp

Os argumentos padrão são vinculados estaticamente, enquanto as funções virtuais são vinculadas dinamicamente. Qual argumento padrão será usado depende do tipo do próprio ponteiro ou referência, e não do tipo do objeto.

4. Pode ou não pode

(1) Uma função estática pode ser declarada virtual?

Há dois motivos principais:

Uma função estática não pode ser declarada virtual, nem pode ser qualificada com as palavras-chave const e volatile

Uma função-membro static não pertence a nenhum objeto ou instância da classe, portanto adicionar virtual a essa função não tem nenhum significado.

As funções virtuais dependem do vptr e da vtable. O vptr é um ponteiro, criado no construtor da classe, e só pode ser acessado por meio do ponteiro this; as funções-membro estáticas não têm ponteiro this e, por isso, não conseguem acessar o vptr.

Código para estudo: static_error.cpp

(2) Um construtor pode ser virtual?

Um construtor não pode ser declarado virtual. Além disso, com exceção de inline e explicit, um construtor não permite o uso de nenhuma outra palavra-chave.

Por que um construtor não pode ser virtual?

Embora a tabela de funções virtuais (vtable) já seja construída na fase de compilação, o ponteiro para a tabela de funções virtuais (vptr) só é gerado em tempo de execução, quando o objeto é instanciado. Se uma classe contém funções virtuais, o compilador adiciona no construtor o código que cria o vptr. Aí está o problema: se o construtor fosse virtual, ele precisaria do vptr para acessar a vtable, mas nesse momento o vptr ainda não existe. Portanto, um construtor não pode ser virtual.

Usamos funções virtuais porque precisamos de despacho polimórfico com informação incompleta. Já o construtor serve para inicializar uma instância, e o tipo da instância precisa ser definido. Por isso não há necessidade de declarar um construtor como virtual.

Código para estudo:

(3) Um destrutor pode ser virtual?

Um destrutor pode ser declarado virtual. Se precisarmos excluir por meio de um ponteiro da classe base um objeto de uma classe derivada, o destrutor deve ser declarado virtual. Na verdade, sempre que uma classe puder ser herdada por outras classes, deve-se declarar um destrutor virtual (mesmo que esse destrutor não faça nada).

Código para estudo:

(4) Uma função virtual pode ser privada?

  • Quando um ponteiro da classe base aponta para um objeto da classe derivada, a função do objeto da classe derivada é chamada;
  • int main() deve ser declarado friend da classe Base, caso contrário a compilação falha. O compilador informa: ptr não consegue acessar a função privada. É claro que, se a classe base for declarada public e a derivada private, o problema deixa de existir.

Código para estudo:

(5) Uma função virtual pode ser inline?

Normalmente o compilador considera se vai tornar inline as funções-membro de uma classe. Mas uma função virtual chamada por meio de um ponteiro ou referência à classe base jamais pode ser inlined. É claro que, quando uma função virtual é chamada em um objeto real ou de forma estática, ela pode ser inlined, e a chamada estática de um destrutor virtual certamente será expandida inline.

  • Uma função virtual pode ser uma função inline; inline pode qualificar uma função virtual, mas uma função virtual não pode ser inlined quando exibe comportamento polimórfico.
  • O inlining é uma sugestão ao compilador em tempo de compilação, enquanto o polimorfismo das funções virtuais ocorre em tempo de execução; o compilador não pode saber qual código será chamado em tempo de execução, por isso uma função virtual não pode ser inlined quando se comporta polimorficamente (em tempo de execução).
  • O único caso em que inline virtual pode ser inlined é quando o compilador sabe a qual classe pertence o objeto chamado (por exemplo Base::who()), o que só acontece quando o compilador tem o objeto real, e não um ponteiro ou referência para o objeto.

Código para estudo:

5. RTTI e dynamic_cast

RTTI (Run-Time Type Identification, identificação de tipo em tempo de execução) permite que um programa, por meio de um ponteiro ou referência a uma classe base, verifique o tipo real da classe derivada do objeto apontado.

Na programação orientada a objetos, às vezes precisamos consultar em tempo de execução se um objeto pode ser usado como um determinado tipo polimórfico. De modo semelhante ao instanceof do Java e aos operadores as e is do C#, o C++ oferece dynamic_cast para conversão dinâmica. Em comparação com as conversões forçadas no estilo C e com o reinterpret_cast do C++, o dynamic_cast fornece verificação de segurança de tipos e é uma conversão baseada em consulta de capacidade (Capability Query); por isso, o dynamic_cast é a escolha recomendada para conversões entre tipos polimórficos.

Código para estudo:

6. Funções virtuais puras e classes abstratas

Veja Funções virtuais puras e classes abstratas

Tópicos relacionados

Comentários