Um dos inscritos do Curso de C++ Moderno questionou a diferença entre Funções-membro Virtuais e Sobrecarga de Funções. Eu respondi, no entanto, os recursos para ilustrar a explicação são meio limitados lá na Udemy. Como são dois assuntos distintos, infelizmente não fiz um vídeo que compara os dois casos.
Acabei enviando alguns links, mas além de não ser em Português, os exemplos de muitos endereços não são tão bons assim. Então, eu preferi criar esse artigo com meus próprios exemplos e ficar mais fácil de entender!
Funções virtuais em C++ são funções membro de uma classe base que podem ser substituídas (overridde
n) por funções membro de uma classe derivada.
Elas são declaradas usando a palavra-chave virtual
na classe base.
O principal objetivo das funções virtuais é permitir o polimorfismo em tempo de execução, ou seja, permitir que a chamada a uma função membro seja resolvida em tempo de execução com base no tipo do objeto para o qual a função está sendo chamada, e não no tipo da referência ou ponteiro que está sendo usado para acessar o objeto.
Aqui estão alguns exemplos para ilustrar o conceito de funções virtuais:
Observação: Funções-membro virtuais NÃO FUNCIONA COM TEMPLATE, e lógico, só funcionam em Programação Orientada a Objetos!
Veja nesse exemplo que a classe Derivada
herda os membros public
(e se houvessem, os protected
também) da classe Base
.
Note que ambas possuem a função-membro: void show()
, e mesmo usando ponteiros(tanto faz ser ou não smart pointers, é a mesma coisa) para criar o objeto, ao chamar show()
, o conteúdo da Base
é que é exibido:
Saída após compilar e rodar g++ nao-virtual.cpp && ./a.out
:
Para conseguirmos exibir o show()
da Derivada
, bastava declararmos a show()
da Base
como virtual
, mas para sabermos que estamos sobreescrvendo uma função-membro virtual, o correto é também declararmos o show()
da Derivada
com a palavra-chave overridde
, ou seja, nosso código ficaria assim:
OBSERVAÇÃO: Somente nos casos em que o objeto é criado com ponteiros, como foi dito acima!
Agora sim, a saída após compilar e rodar g++ nao-virtual.cpp && ./a.out
será o show()
da Derivada
:
Esse conceito também é muito utilizado como Destrutores virtuais, ou seja, para seu software não ficar chamando vários Destrutores recursivamente. Por isso note que eu declarei: ~Base() = default
, mas se tivéssemos declarando como: virtual ~Base();
, o destrutor da Derivada
, se houvesse, prevaleceria!
Isso é importante também para evitar vazamentos de memória e outros problemas relacionados à limpeza adequada dos recursos, principalmente em casos de múltiplas hieraquias de classes, ex,:
class Shape(); → class RectangleShape(); → class SquareShape();
.
Funções virtuais são um mecanismo fundamental para o polimorfismo em C++ e permitem que você escreva código mais flexível e extensível.
Um outro conceito em C++ é Sobrecarga de funções, que em resumo é: “Usar funções de mesmo nome, mas com tipos parâmetros diferentes, mas só diferença de tipo de retorno, não!”.
Exemplo, note que abaixo temos as funções show()
de mesmo nome mas tipos de parâmetros diferentes:
Isso compila e roda!
No entanto se tivéssemos essa função:
Não compilará, pois apesar de ter um tipo de retorno diferente: void
, ela possui os mesmos argumentos que uma função que já existe que é a : std::string show(const std::string& str, int c);
.
Isso funciona também para funções-membro em Programação Orientada a Objetos!
Note que Funções Virtuais é TOTALMENTE diferente de Sobrecarga de funções. Talvez a similaridade que alguns podem ver é o fato de usar mesmo nome de funções.
Espero ter ajudado! E em um futuro próximo pretendo alterar e adicionar vários vezes dos Cursos de C++, com exemplos ainda melhores!