A herança múltipla em C ++ é poderosa, mas uma ferramenta complicada, que geralmente leva a problemas se não for usada com cuidado - problemas como o Problema do Diamante.
Neste artigo, discutiremos o problema do diamante, como ele surge da herança múltipla e o que você pode fazer para resolver o problema.
Herança múltipla em C ++
A herança múltipla é um recurso de Programação Orientada a Objetos (OOP) onde uma subclasse pode herdar de mais de uma superclasse. Em outras palavras, uma classe filha pode ter mais de um pai.
A figura abaixo mostra uma representação pictórica de várias heranças.
No diagrama acima, classe C tem classe A e classe B como seus pais.
Se considerarmos um cenário da vida real, uma criança herda de seu pai e de sua mãe. Portanto, uma criança pode ser representada como uma classe derivada com “Pai” e “Mãe” como seus pais. Da mesma forma, podemos ter muitos exemplos da vida real de herança múltipla.
Na herança múltipla, os construtores de uma classe herdada são executados na ordem em que são herdados. Por outro lado, os destruidores são executados na ordem inversa de sua herança.
Agora vamos ilustrar a herança múltipla e verificar a ordem de construção e destruição de objetos.
Ilustração de código de herança múltipla
Para a ilustração de herança múltipla, programamos exatamente a representação acima em C ++. O código do programa é fornecido abaixo.
#incluir
usando namespace std;
classe A // classe base A com construtor e destruidor
{
público:
A () {cout << "classe A:: Construtor" << endl; }
~ A () {cout << "classe A:: Destruidor" << endl; }
};
classe B // classe base B com construtor e destruidor
{
público:
B () {cout << "classe B:: Construtor" << endl; }
~ B () {cout << "classe B:: Destruidor" << endl; }
};
classe C: público B, público A // classe derivada C herda a classe A e depois a classe B (observe a ordem)
{
público:
C () {cout << "classe C:: Construtor" << endl; }
~ C () {cout << "classe C:: Destruidor" << endl; }
};
int main () {
C c;
return 0;
}
A saída que obtemos do programa acima é a seguinte:
classe B:: Construtor
classe A:: Construtor
classe C:: Construtor
classe C:: Destruidor
classe A:: Destruidor
classe B:: Destruidor
Agora, se verificarmos a saída, veremos que os construtores são chamados na ordem B, A e C, enquanto os destruidores estão na ordem inversa. Agora que sabemos os fundamentos da herança múltipla, passamos a discutir o Problema do Diamante.
O Problema do Diamante, Explicado
O Problema do Diamante ocorre quando uma classe filha herda de duas classes pai que compartilham uma classe de avós em comum. Isso é ilustrado no diagrama abaixo:
Aqui, temos uma aula Filho herdando de classes Pai e Mãe. Essas duas classes, por sua vez, herdam a classe Pessoa porque tanto o pai quanto a mãe são pessoas.
Conforme mostrado na figura, a classe Child herda os traços da classe Person duas vezes - uma vez do pai e novamente da mãe. Isso dá origem a ambigüidade, uma vez que o compilador não consegue entender que caminho seguir.
Este cenário dá origem a um gráfico de herança em forma de diamante e é conhecido como "O Problema do Diamante".
Ilustração de código do problema do diamante
A seguir, representamos o exemplo acima de herança em formato de diamante programaticamente. O código é fornecido abaixo:
#incluir
usando namespace std;
class Person {// class Person
público:
Person (int x) {cout << "Person:: Person (int) chamado" << endl; }
};
class Pai: public Pessoa {// classe Pai herda Pessoa
público:
Pai (int x): Pessoa (x) {
cout << "Pai:: Pai (int) chamado" << endl;
}
};
classe Mãe: public Pessoa {// classe Mãe herda Pessoa
público:
Mãe (int x): Pessoa (x) {
cout << "Mãe:: Mãe (int) chamada" << endl;
}
};
classe Criança: pai público, mãe pública {// A criança herda pai e mãe
público:
Criança (int x): Mãe (x), Pai (x) {
cout << "Criança:: Criança (int) chamada" << endl;
}
};
int main () {
Criança criança (30);
}
A seguir está o resultado deste programa:
Pessoa:: Pessoa (int) chamada
Pai:: Pai (int) chamado
Pessoa:: Pessoa (int) chamada
Mãe:: Mãe (int) chamada
Child:: Child (int) chamado
Agora você pode ver a ambigüidade aqui. O construtor da classe Person é chamado duas vezes: uma vez quando o objeto da classe Pai é criado e a próxima quando o objeto da classe Mãe é criado. As propriedades da classe Person são herdadas duas vezes, gerando ambigüidade.
Como o construtor da classe Person é chamado duas vezes, o destruidor também será chamado duas vezes quando o objeto da classe Child for destruído.
Agora, se você entendeu o problema corretamente, vamos discutir a solução para o Problema do Diamante.
Como corrigir o problema do diamante em C ++
A solução para o problema do diamante é usar o virtual palavra-chave. Transformamos as duas classes pai (que herdam da mesma classe dos avós) em classes virtuais para evitar duas cópias da classe dos avós na classe filha.
Vamos mudar a ilustração acima e verificar o resultado:
Ilustração de código para corrigir o problema do diamante
#incluir
usando namespace std;
class Person {// class Person
público:
Person () {cout << "Person:: Person () chamado" << endl; } // Construtor de base
Person (int x) {cout << "Person:: Person (int) chamado" << endl; }
};
classe Pai: virtual público Pessoa {// classe Pai herda Pessoa
público:
Pai (int x): Pessoa (x) {
cout << "Pai:: Pai (int) chamado" << endl;
}
};
classe Mãe: pessoa pública virtual {// classe Mãe herda pessoa
público:
Mãe (int x): Pessoa (x) {
cout << "Mãe:: Mãe (int) chamada" << endl;
}
};
classe Criança: pai público, mãe pública {// classe A criança herda pai e mãe
público:
Criança (int x): Mãe (x), Pai (x) {
cout << "Criança:: Criança (int) chamada" << endl;
}
};
int main () {
Criança criança (30);
}
Aqui nós usamos o virtual palavra-chave quando as classes Pai e Mãe herdam a classe Person. Isso geralmente é chamado de “herança virtual”, que garante que apenas uma única instância da classe herdada (neste caso, a classe Person) seja passada adiante.
Em outras palavras, a classe Child terá uma única instância da classe Person, compartilhada pelas classes Pai e Mãe. Por ter uma única instância da classe Person, a ambigüidade é resolvida.
A saída do código acima é fornecida a seguir:
Person:: Person () chamado
Pai:: Pai (int) chamado
Mãe:: Mãe (int) chamada
Child:: Child (int) chamado
Aqui você pode ver que o construtor da classe Person é chamado apenas uma vez.
Uma coisa a se notar sobre a herança virtual é que mesmo se o construtor parametrizado do A classe Person é explicitamente chamada pelos construtores das classes Father e Mother por meio da inicialização listas, apenas o construtor base da classe Person será chamado.
Isso ocorre porque há apenas uma única instância de uma classe base virtual que é compartilhada por várias classes que herdam dela.
Para evitar que o construtor base seja executado várias vezes, o construtor de uma classe base virtual não é chamado pela classe que herda dela. Em vez disso, o construtor é chamado pelo construtor da classe concreta.
No exemplo acima, a classe Child chama diretamente o construtor base da classe Person.
Relacionado: Um guia para iniciantes na biblioteca de modelos padrão em C ++
E se você precisar executar o construtor parametrizado da classe base? Você pode fazer isso chamando-o explicitamente na classe Criança em vez de nas classes Pai ou Mãe.
O problema do diamante em C ++, resolvido
O Problema do Diamante é uma ambigüidade que surge na herança múltipla quando duas classes pai herdam da mesma classe avô e ambas as classes pai são herdadas por uma única classe filha. Sem usar herança virtual, a classe filha herdaria as propriedades da classe avô duas vezes, levando à ambigüidade.
Isso pode surgir com frequência no código do mundo real, por isso é importante abordar essa ambigüidade sempre que detectada.
O Problema do Diamante é corrigido usando herança virtual, em que o virtual A palavra-chave é usada quando classes pai herdam de uma classe avô compartilhada. Ao fazer isso, apenas uma cópia da classe dos avós é feita, e a construção do objeto da classe dos avós é feita pela classe filha.
Quer aprender a programar, mas não sabe por onde começar? Esses projetos e tutoriais de programação para iniciantes irão iniciá-lo.
Leia a seguir
- Programação
- Programação C
Assine a nossa newsletter
Junte-se ao nosso boletim informativo para dicas de tecnologia, análises, e-books grátis e ofertas exclusivas!
Clique aqui para se inscrever