Polimorfismo C ++ (función virtual, enlace estático, enlace dinámico, tabla de función virtual, puntero de tabla virtual)

Hoy, eché un vistazo al polimorfismo de C ++ y descubrí que no estaba claro.

¿Qué es el polimorfismo?

Polimorfismo significa literalmente estados múltiples, que aparecerán en clases heredadas. Veamos un ejemplo:

Polimorfismo estático

#include<iostream>
using namespace std;

class A {
    
    
public:
    A():i(10){
    
    }
    void f() {
    
     cout<< "A::f()"<<i<<endl;}
    int i;
};

class B : public A{
    
    
public:
    B():j(20) {
    
    }
    void f()  {
    
     cout << "B::f()" << j <<endl;}
    int j;
};

class C : public A{
    
    
public:
    C():q(30) {
    
    }
    void f()  {
    
     cout << "C::f()" << q <<endl;}
    int q;
};

int main()
{
    
    
	A a;
	B b;
	C c;
	A *p = &b;
	A *q = &c;
	p->f();
	q->f();
	return 0;
}

¿Qué producirá el programa en este caso?
Encontraremos que los resultados son todos A::f()10;
esto se debe a que se ha determinado en el momento de la compilación que la función de la clase base, que es A, se usa como función de salida, por lo que se usa el puntero de la clase base para apuntar al objeto de la subclase seguirá llamando a la función de la clase base, esto es. El enlace estático también se llama polimorfismo estático .
Mira el siguiente ejemplo:

Polimorfismo dinámico

Siendo el mismo programa, agregamos virtual a la función f de la clase base y la definimos como una función virtual.

#include<iostream>
using namespace std;

class A {
    
    
public:
    A():i(10){
    
    }
    virtual void f() {
    
     cout<< "A::f()"<<i<<endl;}
    int i;
};

class B : public A{
    
    
public:
    B():j(20) {
    
    }
    void f()  {
    
     cout << "B::f()" << j <<endl;}
    int j;
};

class C : public A{
    
    
public:
    C():q(30) {
    
    }
    void f()  {
    
     cout << "C::f()" << q <<endl;}
    int q;
};

int main()
{
    
    
	A a;
	B b;
	C c;
	A *p = &b;
	A *q = &c;
	p->f();
	q->f();
	return 0;
}

Los resultados de la operación son:

B::f()20
C::f()30

En este momento, el puntero apunta a la función de la subclase. Esta es la unión dinámica en polimorfismo , también llamado polimorfismo dinámico .

Función virtual

Escribí que después de usar funciones virtuales, se realiza el polimorfismo dinámico A continuación, analicemos lo que sucede en la memoria.
Si generamos un tamaño de clase normal, por ejemplo:

class A{
    
    
	A(){
    
    }
	void print(){
    
    cout<<"1"<<endl;}
}
int main(){
    
    
	cout<<sizeof(A)<<endl;
}

Luego, convierta la función de impresión en una función virtual y muestre su tamaño:

class A{
    
    
	A(){
    
    }
	virtual void print(){
    
    cout<<"1"<<endl;}
}
int main(){
    
    
	cout<<sizeof(A)<<endl;
}

Encontraremos que la clase de funciones virtuales es un poco más grande que la clase de funciones ordinarias.
Debido a que cada objeto de una clase con funciones virtuales tendrá como prefijo un puntero a una tabla que almacena funciones virtuales, lo llamamos vtable, y esta tabla es compartida por todos los objetos.

vtable

Como se mencionó anteriormente, para lograr el polimorfismo, C ++ utiliza un método de enlace dinámico. El núcleo de este método es la vtable (tabla de funciones virtuales). Cada clase con funciones virtuales tiene una tabla de función virtual. Todos los objetos de esta clase comparten una tabla de función virtual. La tabla de función virtual es en realidad una matriz de punteros a funciones virtuales.

Por ejemplo, el siguiente código:

class A {
    
    
public:
    virtual void vfunc1();
    virtual void vfunc2();
    void func1();
    void func2();
private:
    int m_data1, m_data2;
};

Su mesa virtual es la siguiente:

Puntero de tabla virtual

Debido a que la tabla de funciones virtuales pertenece a la clase, todos los objetos comparten una tabla de funciones virtuales. ¿Cómo sabe cada objeto qué tabla de funciones virtuales es? * Cada objeto apunta a su tabla de función virtual a través de un puntero de tabla virtual __vptr.
Inserte la descripción de la imagen aquí

Enlace dinámico

Aquí hay un ejemplo para ver cómo C ++ usa tablas de funciones virtuales y punteros de tablas virtuales para lograr un enlace dinámico.

class A {
    
    
public:
    virtual void vfunc1();
    virtual void vfunc2();
    void func1();
    void func2();
private:
    int m_data1, m_data2;
};

class B : public A {
    
    
public:
    virtual void vfunc1();
    void func1();
private:
    int m_data3;
};

class C: public B {
    
    
public:
    virtual void vfunc2();
    void func2();
private:
    int m_data1, m_data4;
};

Según la relación de herencia se pueden obtener los siguientes resultados:
Inserte la descripción de la imagen aquí

Función virtual y función virtual pura

class A{
    
    
	virtual void A(){
    
    cout<<"A"<<endl;}
	virtual void B()=0; 
}

Lo anterior es un ejemplo de la coexistencia de funciones virtuales y funciones virtuales puras. La diferencia entre ellos es que la función virtual proporciona la implementación de la función, por lo que la subclase puede optar por anular o heredar directamente esta función, mientras que la función virtual pura requiere que la subclase implemente esta función.

Clase abstracta

Una clase con funciones virtuales puras es una clase abstracta, y una clase abstracta es equivalente a la definición de un código de conducta y solo se puede utilizar como clase base. Por ejemplo, si esperamos que la subclase deba completar algunas acciones, escribimos estas acciones como funciones virtuales puras.
Cabe señalar que aquellos que heredan clases abstractas pero no implementan funciones virtuales puras siguen siendo clases abstractas.

Los hombros de los gigantes:
1. Acerca de las tablas de funciones virtuales y los punteros de funciones virtuales

Supongo que te gusta

Origin blog.csdn.net/weixin_45146520/article/details/109537291
Recomendado
Clasificación