Problema familiar a todos: "Por que meu código não funciona?"
\n\nVocê já escreveu uma classe derivada, sobrescreveu um método nela e depois descobriu com surpresa que a funcionalidade básica do pai desapareceu? Familiar? Essa situação é um clássico no mundo do C++. A pergunta "How to call a parent class function from derived class function" no Stack Overflow acumulou milhares de visualizações e centenas de discussões. E não é surpresa: sem entender esse mecanismo, você vai constantemente pisar nos mesmos ancinhos.
\n\nHoje vamos resolver esse assunto de uma vez por todas. Você aprenderá não apenas a sintaxe para chamar a função da classe pai, mas também entenderá quando isso é realmente necessário e quando é prejudicial. Vamos lá!
\n\nBase: herança e sobrescrita de métodos
\n\nAntes de falar sobre chamar funções pai, vamos lembrar como a herança funciona em C++. Imagine que você tem uma classe base Animal com um método speak(). Você cria uma classe Dog que herda de Animal e sobrescreve speak() para que o cachorro lata, em vez de emitir um som genérico.
#include <iostream>\nusing namespace std;\n\nclass Animal {\npublic:\n void speak() {\n cout << "Animal speaks!" << endl;\n }\n};\n\nclass Dog : public Animal {\npublic:\n void speak() {\n cout << "Dog barks!" << endl;\n }\n};\n\nint main() {\n Dog myDog;\n myDog.speak(); // Saída: Dog barks!\n return 0;\n}\n\nTudo funciona. Mas e se você quiser que Dog::speak() primeiro execute a ação do pai e depois adicione a sua? Por exemplo: "O animal emite um som, e o cachorro late". É aí que a coisa fica interessante.
Por que simplesmente chamar o método pai não funciona?
\n\nQuando você sobrescreve um método na classe filha, o C++ considera que você está substituindo completamente o comportamento. O método pai se torna "invisível" dentro do contexto da classe filha. Se você simplesmente escrever speak() dentro de Dog::speak(), o compilador chamará a si mesmo — resultando em recursão infinita.
Solução: operador de resolução de escopo (::)
\n\nPara indicar explicitamente ao compilador: "Ei, quero chamar o método exatamente da classe pai!", usa-se o operador ::. A sintaxe é simples: NomeDaClassePai::nomeDoMetodo().
class Dog : public Animal {\npublic:\n void speak() {\n Animal::speak(); // Chamada do método pai\n cout << "Dog barks!" << endl;\n }\n};\n\nint main() {\n Dog myDog;\n myDog.speak();\n // Saída:\n // Animal speaks!\n // Dog barks!\n return 0;\n}\n\nObserve: não estamos criando um novo objeto pai. Estamos apenas dizendo: "Pegue a implementação de speak() da classe Animal e execute-a no contexto do objeto atual Dog". Este é um mecanismo poderoso que permite construir cadeias de chamadas.
Quando isso é realmente necessário? 3 cenários reais
\n\n1. Extensão de funcionalidade (caso mais comum)
\n\nVocê quer adicionar um novo comportamento sem perder o antigo. Exemplo clássico são construtores e destrutores. Sim, eles são chamados automaticamente, mas se você sobrescrever algum método de inicialização, a chamada ao pai é obrigatória.
\n\nclass Vehicle {\npublic:\n void start() {\n cout << "Engine started. " << endl;\n }\n};\n\nclass Car : public Vehicle {\npublic:\n void start() {\n Vehicle::start(); // Primeiro liga o motor\n cout << "Car is ready to drive!" << endl;\n }\n};\n\n2. Trabalhando com funções virtuais e polimorfismo
\n\nSe o método base for declarado como virtual, chamar a versão pai pode ser útil para implementar um "comportamento base padrão".