A herança é um conceito fundamental na programação orientada a objetos que permite criar novas classes a partir de classes existentes, promovendo a reutilização de código e a redução de redundâncias.
Sintaxe Básica da Herança
Ao definir classes, é comum que subclasses compartilhem características comuns de uma superclasse, mas também tenham propriedades ou métodos próprios. A herança ajuda a eliminar código repetitivo.
Por exemplo, em websites, cabeçalhos e rodapés podem ser encapsulados em uma classe base, enquanto o conteúdo central é personalizado em subclasses.
class ClasseBase {
public:
int atributoPublico;
protected:
int atributoProtegido;
private:
int atributoPrivado;
};
class ClasseDerivada : public ClasseBase {
public:
int atributoEspecifico;
};
Nesta sintaxe, ClasseDerivada é a subclasse (ou classe derivada), e ClasseBase é a superclasse (ou classe base). Os membros da classe derivada incluem tanto os herdados da base quanto os novos.
Modos de Herança
A herança em C++ pode ser pública, protegida ou privada, controlando o acesso aos membros herdados.
class Subclasse : public ClasseBase { };
class Subclasse : protected ClasseBase { };
class Subclasse : private ClasseBase { };
Modelo de Objetos na Herança
Todos os membros não estáticos da classe base são herdados pela classe derivada, incluindo membros privados, que ficam inacessíveis diretamente mas ainda fazem parte do objeto.
class Base {
public:
int x;
protected:
int y;
private:
int z;
};
class Derivada : public Base {
public:
int w;
};
void teste() {
Derivada obj;
// O tamanho do objeto inclui todos os membros herdados (x, y, z) e o próprio w
std::cout << "Tamanho de Derivada: " << sizeof(Derivada) << std::endl;
}
Ordem de Construção e Destruição
Ao criar uma instância de uma classe derivada, o construtor da classe base é chamado primeiro, seguido pelo construtor da derivada. A destruição ocorre na ordem inversa: primeiro a derivada, depois a base.
Tratamento de Membros com Nomes Iguais
Se uma classe derivada tiver membros com o mesmo nome que a classe base, é necessário usar o operador de escopo para acessar os membros da base.
class Base {
public:
int dado;
void funcao() { }
};
class Derivada : public Base {
public:
int dado;
void funcao() { }
};
void teste() {
Derivada obj;
obj.dado; // Acessa o membro da classe derivada
obj.Base::dado; // Acessa o membro da classe base
obj.funcao(); // Chama a função da classe derivada
obj.Base::funcao(); // Chama a função da classe base
}
Se a classe derivada definir uma função com o mesmo nome, todas as versões da classe base são ocultadas, exgiindo o uso do escopo para acesso.
Membros Estáticos com Nomes Iguais
O tratamento para membros estáticos com nomes iguais segue a mesma lógica: acesso direto para a classe derivada e uso de escopo para a classe base, tanto por instância quanto por classe.
class Base {
public:
static int contador;
};
class Derivada : public Base {
public:
static int contador;
};
// Acesso via objeto
obj.Derivada::contador;
obj.Base::contador;
// Acesso via classe
Derivada::contador;
Derivada::Base::contador;
Herança Múltipla
C++ permite herdar de múltiplas classes, mas isso pode causar ambiguidades se as classes base tiverem membros com o mesmo nome. O uso de herança múltipla é geralmente desencorajado devido a sua complexidade.
class ClasseA {
public:
int atributo;
};
class ClasseB {
public:
int atributo;
};
class ClasseC : public ClasseA, public ClasseB {
public:
void metodo() {
// Atributo deve ser especificado com escopo
ClasseA::atributo;
ClasseB::atributo;
}
};
Herança Diamante
A herança diamante ocorre quando duas classes derivam de uma mesma classe base, e uma quarta classe herda dessas duas. Isso leva a duplicação de dados da classe base. A solução é usar herança virtual.
class Animal {
public:
int energia;
};
class Ovelha : virtual public Animal { };
class Camelo : virtual public Animal { };
class Alpaca : public Ovelha, public Camelo { };
// Sem herança virtual, Alpaca teria duas cópias de 'energia'.
// Com herança virtual, apenas uma cópia é mantida.