HOWTO · C++

Sobrecarga de funções vs. sobrescrita de funções em C++

Compare a sobrecarga de funções e a sobrescrita de funções em C++, entendendo como cada mecanismo funciona e quando usar cada um.

Nesta página

A sobrecarga de funções e a sobrescrita de funções resolvem problemas diferentes em C++. A sobrecarga de funções permite declarar funções com o mesmo nome no mesmo escopo, mas com listas de parâmetros diferentes; o compilador escolhe a função adequada com base nos argumentos da chamada. A sobrescrita de funções permite que uma classe derivada substitua a implementação herdada de uma função virtual por outra compatível; quando a chamada é feita por uma referência ou um ponteiro para a classe base, o despacho seleciona em tempo de execução a implementação do objeto derivado.

O que significam sobrecarga e sobrescrita em C++

A sobrecarga permite escolher entre funções que diferem nos parâmetros. A sobrescrita permite substituir um comportamento em uma hierarquia de herança. O tipo de retorno, por si só, não diferencia uma sobrecarga, e a sobrescrita exige que a classe base declare uma função virtual. O especificador override faz o compilador verificar se a declaração da classe derivada realmente corresponde a uma função virtual da classe base.

Como funciona a sobrecarga de funções

Declare funções com o mesmo nome no mesmo escopo e listas de parâmetros distintas. As listas podem diferir no número ou nos tipos dos parâmetros. Ao fazer uma chamada, a resolução de sobrecarga considera os argumentos e os tipos de parâmetro das funções candidatas. O tipo de retorno não é usado para escolher uma sobrecarga; por isso, duas declarações que diferem apenas no tipo de retorno não formam um conjunto válido de sobrecargas.

Execute este exemplo:

O código e os rótulos ou saídas exibidos nesta seção permanecem em inglês.

#include <iostream>

class Formatter {
public:
    void show(int value) const {
        std::cout << "integer: " << value << '\n';
    }

    void show(double value) const {
        std::cout << "decimal: " << value << '\n';
    }
};

int main() {
    Formatter formatter;
    formatter.show(7);
    formatter.show(2.5);
}
integer: 7
decimal: 2.5

As evidências de execução fornecidas confirmam que este exemplo foi executado com sucesso e produziu essa saída. Os argumentos inteiro e decimal selecionam sobrecargas diferentes de show. Ao adicionar uma sobrecarga, verifique se a lista de parâmetros difere de forma significativa e se as chamadas continuam sem ambiguidades.

Como funciona a sobrescrita de funções

A classe base declara uma função virtual; a classe derivada fornece uma função com o mesmo nome e a mesma lista de parâmetros, além de qualificadores e tipo de retorno compatíveis. Marque a declaração derivada com override para que uma incompatibilidade gere um diagnóstico em tempo de compilação. Quando a chamada é feita por uma referência ou um ponteiro para a classe base, o despacho virtual seleciona a implementação final correspondente ao objeto.

Execute este exemplo:

O código e os rótulos ou saídas exibidos nesta seção permanecem em inglês.

#include <iostream>

class Message {
public:
    virtual void print() const {
        std::cout << "message\n";
    }
};

class Alert : public Message {
public:
    void print() const override {
        std::cout << "alert\n";
    }
};

int main() {
    Alert alert;
    const Message& message = alert;
    message.print();
}
alert

As evidências de execução fornecidas confirmam que este exemplo foi executado com sucesso e produziu essa saída. Embora message tenha o tipo da classe base, ele se refere a um Alert; por isso, a chamada virtual usa Alert::print. Uma classe base também pode declarar uma função virtual pura com = 0; essa classe é abstrata e não pode ser instanciada até que classes derivadas concretas implementem suas funções virtuais puras.

Sobrecarga versus sobrescrita: principais diferenças

Aspecto Sobrecarga de funções Sobrescrita de funções
Finalidade Oferecer operações com o mesmo nome para argumentos diferentes Especializar o comportamento herdado em um tipo derivado
Requisito Listas de parâmetros distintas no mesmo escopo Herança e uma função virtual correspondente na classe base
Seleção Resolução de sobrecarga em tempo de compilação, com base nos argumentos da chamada Despacho virtual em tempo de execução por uma referência ou um ponteiro para a classe base
Verificação útil Confirmar que as chamadas identificam a sobrecarga pretendida Adicionar override para verificar se a declaração corresponde à função base

Pode haver sobrecargas sem herança nem funções virtuais. A sobrescrita depende da herança e do despacho virtual; ela não é selecionada pela passagem de uma lista de argumentos diferente.

Erros comuns e como identificá-los

Uma função derivada com o mesmo nome não sobrescreve automaticamente uma função da classe base. Se a função da classe base não for virtual, a declaração derivada oculta o nome da função base, em vez de habilitar o despacho virtual. Se um parâmetro, o qualificador const ou outra parte do tipo da função não corresponder, a sobrescrita pretendida pode falhar; escrever override revela essa incompatibilidade em tempo de compilação.

Não tente criar uma sobrecarga alterando apenas o tipo de retorno. Evite também passar objetos derivados por valor quando espera comportamento polimórfico: copiá-los para um objeto da classe base remove a parte derivada, um efeito conhecido como fatiamento de objeto. Use uma referência ou um ponteiro para a classe base para preservar o despacho dinâmico. Para gerenciar objetos normalmente, prefira objetos automáticos ou ponteiros inteligentes; se excluir um objeto derivado por meio de um ponteiro para a classe base, essa classe deve ter um destrutor virtual.