virtuales puro poo polimorfismo modificador herencia funciones ejemplo codigo c++ inheritance compiler-construction virtual-inheritance

puro - modificador virtual c++



¿Cómo se implementa la herencia virtual de C++ en los compiladores? (5)

Como se mencionó anteriormente, depende de la implementación del compilador.

Pero, generalmente cada vez que un programador agrega un nuevo método, se almacena en el código, incluso si hay otro método con el mismo ID. en otro lugar ("anulado" o "sobrecargado").

El código para cada método se almacena solo una vez, por lo que si una clase hereda y usa el mismo método de una clase padre, internamente, usa un puntero al código, no duplica el código.

Si una clase principal define un método virtual, y si una clase secundaria lo reemplaza, ambos métodos se almacenan. Cada clase tiene algo llamado "Tabla de métodos virtuales" donde hay una tabla de punteros para cada método.

No se preocupe por el rendimiento, el compilador no duplica el código de los métodos.

¿Cómo implementan los compiladores la herencia virtual?

En el siguiente código:

class A { public: A(int) {} }; class B : public virtual A { public: B() : A(1) {} }; class C : public B { public: C() : A(3), B() {} };

¿Un compilador genera dos instancias de función B::ctor , una sin llamada A(1) y otra con ella? Entonces, cuando se llama a B::constructor desde el constructor de la clase derivada, se usa la primera instancia, de lo contrario, la segunda.


Depende de la implementación. GCC (vea esta pregunta ), por ejemplo, emitirá dos constructores, uno con una llamada a A(1) y otra sin él.

B1() B2() // no A

Cuando se construye B, se llama la versión "completa":

B1(): A(1) B() body

Cuando C se construye, la versión base se llama en su lugar:

C(): A(3) B2() B() body C() body

De hecho, se emitirán dos constructores incluso si no hay herencia virtual, y serán idénticos.


El Itanium C ++ ABI es un recurso útil para todas las preguntas, como "¿cómo podría implementar esto los compiladores de C ++?".

En particular, 5.1.4. Otras funciones especiales y entidades enumeran diferentes funciones de miembros especiales para diferentes propósitos:

<ctor-dtor-name> ::= C1 # complete object constructor ::= C2 # base object constructor ::= C3 # complete object allocating constructor ::= D0 # deleting destructor ::= D1 # complete object destructor ::= D2 # base object destructor

La sección 1.1 Definiciones es útil (pero no completa):

objeto de base destructor de una clase T

Una función que ejecuta los destructores para miembros de datos no estáticos de T y clases base directas no virtuales de T.

destructor de objeto completo de una clase T

Una función que, además de las acciones requeridas de un destructor de objetos base, ejecuta los destructores para las clases base virtuales de T.

eliminar el destructor de una clase T

Una función que, además de las acciones requeridas de un destructor de objeto completo, llama a la función de desasignación apropiada (es decir, eliminación de operador) para T.

A partir de estas definiciones, el objetivo del constructor de objetos completo y del constructor de objetos base es obvio.


El compilador no crea otro constructor de B, pero ignora el A(1) . Como A prácticamente se hereda, se construye primero, con su constructor predeterminado. Y dado que ya está construido cuando se invoca B() , la parte A(1) se ignora.

Editar - Extrañé la parte A(3) en la lista de inicialización del constructor de C Cuando se usa la herencia virtual, solo la clase más derivada inicializa las clases base virtuales. Entonces A se construirá con A(3) y no su constructor predeterminado. El resto sigue en pie: se ignoran todas las inicializaciones de A por una clase intermedia (aquí B ).

Edite 2, tratando de responder la pregunta real con respecto a la implementación de lo anterior:

En Visual Studio (al menos 2010), se usa un indicador en lugar de tener dos implementaciones de B() . Como B hereda virtualmente de A , antes de llamar al constructor de A , se marca el indicador. Si el indicador no está configurado, la llamada a A() se omite. Luego, en cada clase derivada de B , la bandera se reinicia después de inicializar A El mismo mecanismo se usa para evitar que C inicialice A si es parte de alguna D (si D hereda de C , D inicializará A ).


Te sugiero que leas algunos documentos. Estos dos son realmente interesantes, especialmente el primero ya que proviene del padre de C ++:

[1] Bjarne Stroustrup. Herencia múltiple para C ++. El C / C ++ Users Journal, mayo de 1999.

[2] J. Templ. Un enfoque sistemático para la implementación de herencia múltiple. Avisos de ACM SIGPLAN, Volumen 28, No. 4, abril de 1993.

Los usé como referencias principales mientras hacía un seminario (como estudiante) sobre herencia múltiple en mi universidad.