Estoy estudiando la herencia de C ++ y escribí este código:
// 08 Diamond Inheritance 02.cpp : Defines the entry point for the console application.
// I'm using Microsoft Visual Studio 2015
#include <iostream>
using namespace std;
class SuperVirtual
{
public:
int sv;
SuperVirtual(int p1 = 11) : sv(p1)
{
cout << "\n SuperVirtual ctor for &" << this;
}
virtual void methodSuperVirtual_01()
{
cout << "\n Inside SuperVirtual::methodSuperVirtual_01(): sv = " << ++sv;
}
virtual void methodSuperVirtual_02()
{
cout << "\n Inside SuperVirtual::methodSuperVirtual_02(): sv = " << ++sv;
}
};
//----------------------------------
class DerivedSV_01 : public SuperVirtual
{
public:
int dsv;
DerivedSV_01(int p1 = 21) : dsv(p1)
{
cout << "\n DerivedSV_01 ctor for &" << this;
}
virtual void methodSuperVirtual_01() override
{
cout << "\n Inside DerivedSV_01::methodSuperVirtual_01()";
}
//----------------------------------------------------
virtual void onlyForDerivedSV_01()
{
cout << "\n Inside DerivedSV_01::onlyForDerivedSV_01()";
}
};
int main()
{
SuperVirtual sv1(1);
sv1.methodSuperVirtual_01();
DerivedSV_01 dsv_01;
dsv_01.methodSuperVirtual_01();
dsv_01.onlyForDerivedSV_01();
return 0;
}
Había leído eso:
Siempre que una clase contenga funciones virtuales o anule funciones virtuales de una clase primaria, el compilador construye una vtable para esa clase. El vtable contiene punteros de función que apuntan a las funciones virtuales en esa clase. Solo puede haber una vtable por clase, y todos los objetos de la misma clase compartirán la misma vtable.
En este punto, supuse que la tabla virtual de la clase SuperVirtual
contiene 2 elementos, mientras que la tabla virtual para la clase DerivedSV_01
contiene 3 elementos: 2 para los métodos heredados (y eventualmente superados) + 1 para el nuevo método virtual onlyForDerivedSV_01()
.
Sin embargo, al inspeccionar los objetos creados en main()
con el depurador de Visual Studio, descubrí que:
La tabla virtual, por ejemplo, la clase
SuperVirtual
contiene 2 elementos (¡OK!)La tabla virtual, por ejemplo, la clase
DerivedSV_01
contiene 2 elementos ( ¿Por qué? )
Para completar la información, proporciono la siguiente captura de pantalla:
2 respuestas
Puedo reproducir esto con exactamente su código en VS2019 al compilar en modo de depuración (optimización del compilador desactivada). Estoy bastante seguro de que esto es solo un problema de visualización en la ventana del depurador.
Tenga en cuenta que la matriz __vfptr
solo se muestra debajo de SuperVirtual
, no directamente debajo de dsv_01
, por lo que, por supuesto, el depurador solo conoce las funciones en una tabla SuperVirtual
.
Pero si mira la columna Value
para __vfptr
, notará que en SuperVirtual::'vftable'[3]
y DerivedSV_01::'vftable[4]'
(ambos marcados en verde) se dan diferentes tamaños de matriz.
Echemos un vistazo a la memoria de las tablas v (las direcciones de las tablas v se pueden ver al comienzo de la columna Value
para las entradas __vfptr
y están marcadas en naranja y rojo).
Notarás que SuperVirtual::'vftable'[3]
tiene dos punteros de función (marcados en marrón) y un nullptr
. DerivedSV_01::'vftable[4]'
tiene tres punteros (marcados morado y azul) y un nullptr
. La ventana del depurador nos dice cuáles son las dos primeras entradas (también marcadas en púrpura), pero eche un vistazo a la tercera entrada en la ventana de observación (marcada en azul).
El depurador dice que la tercera entrada es DerivedSV_01::onlyForDerivedSV_01
. Eso coincide totalmente con tus (y mis) expectativas.
La respuesta es simple "COMO SI" entró en vigor la regla.
El compilador descubre que onlyForDerivedSV_01
nunca se usa de forma polimórfica (ya que no hay subclases de DerivedSV_01
), por lo que se ha convertido al método normal.
Como resultado, solo tiene dos métodos virtuales heredados de la clase base.
De hecho, con la optimización completa, el compilador habilitado debería poder eliminar todas las llamadas virtuales para este código. Aquí puede ver todos los métodos se colocan directamente en la función main
y solo se realizan llamadas virtuales en std::cout
.
Nuevas preguntas
c++
C ++ es un lenguaje de programación de propósito general. Originalmente fue diseñado como una extensión de C, y tiene una sintaxis similar, pero ahora es un lenguaje completamente diferente. Use esta etiqueta para preguntas sobre el código (que se compilará) con un compilador de C ++. Utilice una etiqueta específica de la versión para preguntas relacionadas con una revisión estándar específica [C ++ 11], [C ++ 14], [C ++ 17] o [C ++ 20] etc.