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
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