tipos relaciones programacion polimorfismo orientada objetos multiple herencia entre clases clase abstracta c++ multiple-inheritance sizeof

relaciones - Pregunta sobre herencia múltiple, clases base virtuales y tamaño de objeto en C++



relaciones entre clases programacion orientada a objetos (3)

El siguiente código imprime 20, es decir, sizeof (z) es 20.

#include <iostream.h> class Base { public: int a; }; class X:virtual public Base { public: int x; }; class Y:virtual public Base { public: int y; }; class Z:public X,public Y { }; int main() { Z z; cout << sizeof(z) <<endl; }

Mientras que si no uso clases base virtuales aquí, es decir, para el siguiente código: sizeof (z) es 16.

#include <iostream.h> class Base { public: int a; }; class X:public Base { public: int x; }; class Y:public Base { public: int y; }; class Z:public X,public Y { }; int main() { Z z; cout << sizeof(z) <<endl; }

¿Por qué sizeof (z) es más (20) en el primer caso? ¿No debería ser 12, ya que Base se incluirá solo una vez en Z?



La respuesta de Mark Santesson se basa en el dinero, pero la afirmación de que no hay tablas es incorrecta. Puede usar g ++ -fdump-class-hierarchy para mostrar lo que está sucediendo. Aquí está el caso de no virtuales:

Class Base size=4 align=4 base size=4 base align=4 Base (0x19a8400) 0 Class X size=8 align=4 base size=8 base align=4 X (0x19a8440) 0 Base (0x19a8480) 0 Class Y size=8 align=4 base size=8 base align=4 Y (0x19a84c0) 0 Base (0x19a8500) 0 Class Z size=16 align=4 base size=16 base align=4 Z (0x19b1800) 0 X (0x19a8540) 0 Base (0x19a8580) 0 Y (0x19a85c0) 8 Base (0x19a8600) 8

Preste especial atención al argumento de "tamaño de base". Ahora el caso de los virtuales, y mostrando solo Z:

Class Z size=20 align=4 base size=16 base align=4 Z (0x19b3000) 0 vptridx=0u vptr=((& Z::_ZTV1Z) + 12u) X (0x19a8840) 0 primary-for Z (0x19b3000) subvttidx=4u Base (0x19a8880) 16 virtual vbaseoffset=-0x0000000000000000c Y (0x19a88c0) 8 subvttidx=8u vptridx=12u vptr=((& Z::_ZTV1Z) + 24u) Base (0x19a8880) alternative-path

Tenga en cuenta que el "tamaño base" es el mismo, pero el "tamaño" es un puntero más, y tenga en cuenta que ahora hay un puntero vtable. Esto a su vez contiene los vtables de construcción para las clases padre, y toda la magia entre clases (vtables de construcción y tabla de tabla virtual (VTT)), como se describe aquí:

http://www.cse.wustl.edu/~mdeters/seminar/fall2005/mi.html

Tenga en cuenta que el vtable de la función real vtable estará vacío.


Veamos el diseño de clase de los dos casos.

Sin lo virtual, tiene dos clases base ("X" e "Y") con un entero cada una, y cada una de esas clases ha integrado en ellas una clase base "Base" que también tiene un número entero. Eso es 4 enteros, 32 bits cada uno, totalizando sus 16 bytes.

Offset Size Type Scope Name 0 4 int Base a 4 4 int X x 8 4 int Base a 12 4 int Y y 16 size (Z members would come at the end)

(Editar: escribí un programa en DJGPP para obtener el diseño y pellizqué la tabla para dar cuenta de ello).

Ahora hablemos de clases base virtuales: reemplazan la instancia real de la clase con un puntero a una instancia compartida. Su clase "Z" tiene solo una clase "Base", y ambas instancias de "X" e "Y" lo señalan. Por lo tanto, tiene números enteros en X, Y y Z, pero solo tiene la Z. Eso significa que tiene tres enteros o 12 bytes. Pero X e Y también tienen un puntero a la Z compartida (de lo contrario, no sabrían dónde encontrarla). En una máquina de 32 bits, dos punteros agregarán 8 bytes adicionales. Esto suma los 20 que ves. El diseño de la memoria puede parecerse a esto (no lo he verificado ... el ARM tiene un ejemplo donde el orden es X, Y, Z, luego Base):

Offset Size Type Scope Name Value (sort of) 0 4 Base offset X ? 16 (or ptr to vtable) 4 4 int X x 8 4 Base offset Y ? 16 (or ptr to vtable) 12 4 int Y y 16 4 int Base a 20 size (Z members would come before the Base)

Entonces la diferencia de memoria es una combinación de dos cosas: un entero menos y dos punteros más. Contrariamente a otra respuesta, no creo que los vtables paguen ninguna (edición) directa (/ edición) en esto, ya que no hay funciones virtuales.

Editar: ppinsider ha proporcionado más información sobre el caso gcc, en el que demuestra que gcc implementa el puntero a la clase base virtual haciendo uso de un vtable vacío (es decir, sin funciones virtuales). De esta forma, si hubiera funciones virtuales, no requeriría un puntero adicional en la instancia de la clase, lo que requeriría más memoria. Sospecho que la desventaja es una indirección adicional para llegar a la clase base.

Podríamos esperar que todos los compiladores hicieran esto, pero quizás no. El ARM página 225 discute clases base virtuales sin mencionar vtables. La página 235 aborda específicamente "clases base virtuales con funciones virtuales" y tiene un diagrama que indica un diseño de memoria donde hay punteros de las partes X e Y que están separadas de los punteros a la tabla virtual. Aconsejaría a cualquiera que no dé por sentado que el puntero a Base se implementará en términos de una tabla.