Herencia multiple en C++

Hola que tal,
estamos trabajando en C++ y tenemos el siguiente esquema:
Una clase A que es abstracta, de ella se derivan dos clases concretas, B
y C.
Finalmente existe la clase DE que hereda de B y C.
Usamos funciones virtuales pero al querer mostrar los datos desde DE encontramos que igualmente repite dos veces los datos de la clase A.
Gracias
Mariano
A
/ \
B C
\ /
D

1 respuesta

Respuesta
1
La herencia múltiple y sus quebraderos de cabeza...
Realmente tienes dos veces los datos de A, por eso aparecen.
DE tiene sus datos, los de B porque deriva de B y los de A porque deriva de A.
Por otro lado B tiene los datos del objeto de la clase C porque deriva de la clase C, y todo objeto de C tiene los atributos de A.
Tienes dos veces los atributos de A, y tienes dos tablas de punteros a funciones virtuales de A, lo que te puede dar grandes problemas.
Para verlo más fácil imagina el siguiente diagrama de herencia
A A'
| |
B C
\ /
D
En este caso esperarías tener los atributos de A y de A': pues es lo que tienes.
El problema más extraño de la herencia múltiple es el siguiente:
La clase A ocupa 10 bytes, la clase B ocupa 20 bytes por sí sola (sin contar con los 10 que tiene de la clase A), la clase C 30 bytes y la clase D 40 bytes.
A:10, B:20, C:30, D:40
Un objeto de la clase B ocupa realmente 10+20=30 bytes, uno de la clase C ocupa 10+30=40 y un objeto de la clase D ocupa 10+20+10+30+40 = 110 bytes.
No es lo que os gustaría en este momento pero realmente ocupa 110 bytes. Vale.
Has dicho que la clase A es abstracta, luego seguramente tienes por ahí una función que tiene como parámetro un puntero a A.
miFuncion(A* param);
Si pasas como parámetro un objeto de la clase B la estructura que le pasas es de 30 bytes. Si pasas un objeto de la clase C, dentro de miFuncion tienes un objeto con 40 bytes y si le pasas D puedes tener una objeto de 110 bytes o puedes tener un objeto de 80 bytes.
¿Por qué? Por que estás pasando un puntero a A y toma A y todo lo de la clase derivada. Si toma el camino de A coge bien la estructura, pero si toma el camino de A' se encuentra con A':10 + C:30 + D:40 = 80.
Eso se traduce en que cuando intentes acceder a una variable pasado el byte 80 ... no vas a tener datos.
¿Cómo puedes comprobar esto? Créate esa función miFuncion e imprime (con un simple printf o un <<) dentro los valores de los atriutos. P.ej:
// Aunque se pasa A* vamos a pasar siempre un objeto de la clase D
miFuncion (A* param)
{
imprimir los atributos de param (clase A) // linea 1
B* b = (B*) param; // linea 2
imprimir los atributos de b (clase B) // linea 3
C* c = (C*) param; // linea 4
imprimir los atributos de c (clase C) // linea 5
D* d = (D*) param; // linea 6
imprimir los atributos de d (clase D) // linea 7
}
A la hora de llamar a miFuncion hazlo con
// Creamos e inicializamos objD
D* objD = new D(v1, v2, v3, ...); // Este es el unico objeto que se crea
// Mostramos todos los atributos de objD
miFuncion(objD);
B* objB = (B*) objD; // El objeto objD visto como objeto de la clase B
// Mostramos todos los atributos de objD por el camino de la clase B: OK
miFuncion(objB);
C* objC = (C*) objD; // El objeto objD visto como objeto de la clase C
// Mostramos todos los atributos de objD por el camino de la clase C
miFuncion(objC);
Este último caso falla. ObjC apunta a una estructura de 80 bytes compuesta por atributos de las clases A', C y D.
En la linea 1 mostrará correctamente los valores de A' (la clase A de la que deriva C).
En la linea 3 mostrará el contenido de la clase C como si estuviera accediendo a la clase B.
En la linea 5, cuando acceda más allá del byte 80 nadie puede predecir su comportamiento (dependerá del compilador y del sistema operativo).
Moraleja:
Nunca o sólo en casos estrictamente necesarios se debe utilizar la herencia múltiple. De hecho muchos lenguajes la han suprimido deliberadamente por ser una fuente inagotable de errores.
Solución:
En su lugar D puede derivar de B y tener un puntero a un objeto de la clase C, pero no vas a poder garantizar que las partes A de la clase B y la parte A de la clase C (o sea, A') sean i

Añade tu respuesta

Haz clic para o

Más respuestas relacionadas