La herencia múltiple en C ++ es poderosa, pero una herramienta complicada, que a menudo genera problemas si no se usa con cuidado, problemas como el problema del diamante.

En este artículo, analizaremos el problema del diamante, cómo surge de la herencia múltiple y qué puede hacer para resolver el problema.

Herencia múltiple en C ++

La herencia múltiple es una característica de la programación orientada a objetos (OOP) donde una subclase puede heredar de más de una superclase. En otras palabras, una clase secundaria puede tener más de un padre.

La siguiente figura muestra una representación pictórica de múltiples herencias.

En el diagrama de arriba, clase C tiene clase A y clase B como sus padres.

Si consideramos un escenario de la vida real, un niño hereda de su padre y su madre. Por tanto, un Niño puede representarse como una clase derivada con "Padre" y "Madre" como sus padres. De manera similar, podemos tener muchos ejemplos de la vida real de herencia múltiple.

En la herencia múltiple, los constructores de una clase heredada se ejecutan en el orden en que se heredan. Por otro lado, los destructores se ejecutan en el orden inverso de su herencia.

instagram viewer

Ahora ilustremos la herencia múltiple y verifiquemos el orden de construcción y destrucción de los objetos.

Ilustración de código de herencia múltiple

Para la ilustración de herencia múltiple, hemos programado exactamente la representación anterior en C ++. El código del programa se proporciona a continuación.

#incluir
usando el espacio de nombres std;
clase A // clase base A con constructor y destructor
{
público:
A () {cout << "clase A:: Constructor" << endl; }
~ A () {cout << "clase A:: Destructor" << endl; }
};
clase B // clase base B con constructor y destructor
{
público:
B () {cout << "clase B:: Constructor" << endl; }
~ B () {cout << "clase B:: Destructor" << endl; }
};
clase C: público B, público A // la clase C derivada hereda la clase A y luego la clase B (observe el orden)
{
público:
C () {cout << "clase C:: Constructor" << endl; }
~ C () {cout << "clase C:: Destructor" << endl; }
};
int main () {
C c;
return 0;
}

La salida que obtenemos del programa anterior es la siguiente:

clase B:: Constructor
clase A:: Constructor
clase C:: Constructor
clase C:: Destructor
clase A:: Destructor
clase B:: Destructor

Ahora, si comprobamos la salida, vemos que los constructores se llaman en el orden B, A y C mientras que los destructores están en el orden inverso. Ahora que conocemos los conceptos básicos de la herencia múltiple, pasamos a discutir el problema del diamante.

El problema del diamante, explicado

El problema del diamante ocurre cuando una clase secundaria hereda de dos clases principales que comparten una clase de abuelos común. Esto se ilustra en el diagrama siguiente:

Aquí tenemos una clase Niño heredando de clases Padre y Madre. Estas dos clases, a su vez, heredan la clase Persona porque tanto el Padre como la Madre son Persona.

Como se muestra en la figura, la clase Niño hereda los rasgos de la clase Persona dos veces: una del Padre y otra de la Madre. Esto da lugar a ambigüedad ya que el compilador no comprende qué camino tomar.

Este escenario da lugar a un gráfico de herencia en forma de diamante y se conoce como "El problema del diamante".

Ilustración de código del problema del diamante

A continuación, hemos representado el ejemplo anterior de herencia en forma de diamante mediante programación. El código se proporciona a continuación:

#incluir
usando el espacio de nombres std;
class Person {// clase Person
público:
Persona (int x) {cout << "Persona:: Persona (int) llamada" << endl; }
};
class Father: public Person {// class Father hereda Person
público:
Padre (int x): Persona (x) {
cout << "Padre:: Padre (int) llamado" << endl;
}
};
class Mother: Public Person {// class Mother hereda Person
público:
Madre (int x): Persona (x) {
cout << "Madre:: Madre (int) llamada" << endl;
}
};
class Child: public Father, public Mother {// El hijo hereda el padre y la madre
público:
Hijo (int x): Madre (x), Padre (x) {
cout << "Child:: Child (int) llamado" << endl;
}
};
int main () {
Niño niño (30);
}

A continuación se muestra el resultado de este programa:

Persona:: Persona (int) llamada
Padre:: Padre (int) llamado
Persona:: Persona (int) llamada
Madre:: Madre (int) llamada
Child:: Child (int) llamado

Ahora puedes ver la ambigüedad aquí. El constructor de la clase Person se llama dos veces: una vez cuando se crea el objeto de la clase Father y luego cuando se crea el objeto de la clase Mother. Las propiedades de la clase Person se heredan dos veces, lo que genera ambigüedad.

Dado que el constructor de la clase Person se llama dos veces, el destructor también se llamará dos veces cuando se destruya el objeto de la clase Child.

Ahora, si ha entendido el problema correctamente, analicemos la solución al problema del diamante.

Cómo solucionar el problema del diamante en C ++

La solución al problema del diamante es utilizar el virtual palabra clave. Convertimos las dos clases principales (que heredan de la misma clase de abuelos) en clases virtuales para evitar dos copias de la clase de abuelos en la clase secundaria.

Cambiemos la ilustración anterior y verifiquemos el resultado:

Ilustración de código para solucionar el problema del diamante

#incluir
usando el espacio de nombres std;
class Person {// clase Person
público:
Persona () {cout << "Persona:: Persona () llamada" << endl; } // Constructor base
Persona (int x) {cout << "Persona:: Persona (int) llamada" << endl; }
};
class Father: público virtual Person {// class Father hereda Person
público:
Padre (int x): Persona (x) {
cout << "Padre:: Padre (int) llamado" << endl;
}
};
clase Madre: Persona pública virtual {// clase Madre hereda Persona
público:
Madre (int x): Persona (x) {
cout << "Madre:: Madre (int) llamada" << endl;
}
};
class Child: public Father, public Mother {// class Child hereda Father and Mother
público:
Hijo (int x): Madre (x), Padre (x) {
cout << "Child:: Child (int) llamado" << endl;
}
};
int main () {
Niño niño (30);
}

Aquí hemos utilizado el virtual palabra clave cuando las clases Padre y Madre heredan la clase Persona. Esto generalmente se denomina "herencia virtual", lo que garantiza que solo se transmita una única instancia de la clase heredada (en este caso, la clase Person).

En otras palabras, la clase Child tendrá una única instancia de la clase Person, compartida por las clases Father y Mother. Al tener una sola instancia de la clase Person, se resuelve la ambigüedad.

La salida del código anterior se da a continuación:

Persona:: Persona () llamada
Padre:: Padre (int) llamado
Madre:: Madre (int) llamada
Child:: Child (int) llamado

Aquí puede ver que el constructor de la clase Person se llama solo una vez.

Una cosa a tener en cuenta sobre la herencia virtual es que incluso si el constructor parametrizado del Los constructores de clases Father y Mother llaman explícitamente a la clase Person mediante la inicialización liza, solo se llamará al constructor base de la clase Person.

Esto se debe a que solo hay una instancia única de una clase base virtual compartida por varias clases que heredan de ella.

Para evitar que el constructor base se ejecute varias veces, la clase que la hereda no llama al constructor de una clase base virtual. En cambio, el constructor de la clase concreta llama al constructor.

En el ejemplo anterior, la clase Child llama directamente al constructor base para la clase Person.

Relacionado: Una guía para principiantes de la biblioteca de plantillas estándar en C ++

¿Qué sucede si necesita ejecutar el constructor parametrizado de la clase base? Puede hacerlo llamándolo explícitamente en la clase Niño en lugar de en las clases Padre o Madre.

El problema del diamante en C ++, resuelto

El problema del diamante es una ambigüedad que surge en la herencia múltiple cuando dos clases principales heredan de la misma clase abuela y ambas clases principales son heredadas por una sola clase secundaria. Sin el uso de la herencia virtual, la clase secundaria heredaría las propiedades de la clase abuelo dos veces, lo que generaría ambigüedad.

Esto puede surgir con frecuencia en el código del mundo real, por lo que es importante abordar esa ambigüedad cada vez que se detecta.

El problema del diamante se soluciona mediante la herencia virtual, en la que el virtual La palabra clave se usa cuando las clases padre heredan de una clase abuelo compartida. Al hacerlo, solo se realiza una copia de la clase de abuelos, y la clase secundaria realiza la construcción del objeto de la clase de abuelos.

CuotaPíoCorreo electrónico
Los 10 mejores proyectos para principiantes para nuevos programadores

¿Quieres aprender a programar pero no sabes por dónde empezar? Estos proyectos y tutoriales de programación para principiantes le ayudarán a empezar.

Leer siguiente

Temas relacionados
  • Programación
  • Programación en C
Sobre el Autor
Personal de MUO

Suscríbete a nuestro boletín

¡Únase a nuestro boletín de noticias para obtener consejos técnicos, reseñas, libros electrónicos gratuitos y ofertas exclusivas!

Haga clic aquí para suscribirse