Punteros a funciones miembro virtuales. ¿Cómo funciona?


Considere el siguiente código C++:

class A
{
public:
      virtual void f()=0;
};


int main()
{
     void (A::*f)()=&A::f;
}

Si tuviera que adivinar, diría que &A::f en este contexto significaría "la dirección de la implementación de A de f()", ya que no hay separación explícita entre punteros a funciones miembro regulares y funciones miembro virtuales. Y como A no implementa f (), eso sería un error de compilación. Sin embargo, no lo es.

Y no solo eso. El código siguiente:

void (A::*f)()=&A::f;
A *a=new B;            // B is a subclass of A, which implements f()
(a->*f)();

Llamará a B:: f.

¿Cómo lo hace ¿pasar?

Author: Steve Jessop, 2009-07-06

3 answers

Aquí hay demasiada información sobre los punteros de función miembro. Hay algunas cosas sobre funciones virtuales bajo "Los Compiladores de Buen comportamiento", aunque IIRC cuando leí el artículo estaba hojeando esa parte, ya que el artículo es en realidad sobre la implementación de delegados en C++.

Http://www.codeproject.com/KB/cpp/FastDelegate.aspx

La respuesta corta es que depende del compilador, pero una posibilidad es que el puntero de la función miembro se implemente como un estructura que contiene un puntero a una función "thunk" que hace la llamada virtual.

 10
Author: Steve Jessop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-06 15:36:20

Funciona porque el Estándar dice que así es como debe suceder. Hice algunas pruebas con GCC, y resulta que para las funciones virtuales, GCC almacena el desplazamiento de la tabla virtual de la función en cuestión, en bytes.

struct A { virtual void f() { } virtual void g() { } }; 
int main() { 
  union insp { 
    void (A::*pf)();
    ptrdiff_t pd[2]; 
  }; 
  insp p[] = { { &A::f }, { &A::g } }; 
  std::cout << p[0].pd[0] << " "
            << p[1].pd[0] << std::endl;
}

Ese programa produce 1 5 - las compensaciones de bytes de las entradas de la tabla virtual de esas dos funciones. Sigue el Itanium C++ ABI, que especifica que.

 22
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-06 15:38:27

No estoy del todo seguro, pero creo que es solo un comportamiento polimórfico regular. Creo que &A::f en realidad significa la dirección del puntero de la función en la vtable de la clase, y es por eso que no está recibiendo un error de compilador. El espacio en la vtable todavía está asignado, y esa es la ubicación que realmente está recuperando.

Esto tiene sentido porque las clases derivadas esencialmente sobrescriben estos valores con punteros a sus funciones. Es por eso que (a->*f)() funciona en su segundo ejemplo - f hace referencia a la vtable que se implementa en la clase derivada.

 1
Author: Cristián Romo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-06 15:40:11