Puntero de C++ a la función virtual

4 minutos de lectura

Si tienes una estructura como esta

struct A {
    void func();
};

y una referencia como esta

A& a;

puede obtener un puntero a su func método como este:

someMethod(&A::func);

Ahora, ¿qué sucede si ese método es virtual y no sabe qué es en tiempo de ejecución? ¿Por qué no puedes obtener un puntero como este?

someMethod(&a.func);

¿Es posible obtener un puntero a ese método?

Los punteros a miembros tienen en cuenta la virtualidad de las funciones a las que apuntan. Por ejemplo:

#include <iostream>
struct Base
{
    virtual void f() { std::cout << "Base::f()" << std::endl; }
};

struct Derived:Base
{
    virtual void f() { std::cout << "Derived::f()" << std::endl; }
};


void SomeMethod(Base& object, void (Base::*ptr)())
{
    (object.*ptr)();    
}


int main()
{
    Base b;
    Derived d;
    Base* p = &b;
    SomeMethod(*p, &Base::f); //calls Base::f()
    p = &d;
    SomeMethod(*p, &Base::f); //calls Derived::f()    
}

Salidas:

Base::f()
Derived::f()

  • ¡Gracias! No me di cuenta de que un puntero de función miembro tendría eso en cuenta.

    – Chris

    19 de julio de 2011 a las 22:25

  • @Chris: los punteros de funciones de miembros son inteligentes. Es por eso que pueden ser más grandes que los punteros de función habituales. Incluso tienen en cuenta el desplazamiento de un subobjeto de clase base en caso de herencia múltiple

    – Armen Tsirunyan

    19 de julio de 2011 a las 22:30


avatar de usuario
Kerrek SB

La forma de invocar un puntero de función es proporcionar también el puntero de instancia de su objeto. Esto se encargará de todos los problemas de virtualidad:

struct A { void func(); };

int main()
{
  typedef void (A::*mf)();

  A x; // irrelevant if A is derived or if func is virtual

  mf f = &A::func;   // pointer-to-member-function
  A* p = &x;         // pointer-to-instance

  (p->*f)();           // invoke via pointer
  (x.*f)();            // invoke directly
}

bien, interesante desafío de sintaxis pregunta: Supongamos que tengo esto.

struct Basil { virtual void foo(); virtual ~Basil(); };
struct Derrek : public Basil { virtual void foo(); };

Ahora si tengo Derrek * p o un Basil * ppuedo invocar el Basil miembro a través de p->Basil::foo(). ¿Cómo podría hacer lo mismo si me dieran un void(Derrek::*q)() = &Derrek::foo?

Responder: No se puede hacer. el PMF q alone no sabe si apunta a una función virtual, y mucho menos a cuál, y no se puede usar para buscar una función de clase base en tiempo de ejecución. [Thanks to Steve and Luc!]

  • Creo que te refieres a p->*f(). (pero ahora digamos que func es virtual y podría ser A::func o B::func; no sabe qué es. ¿Cómo puede obtener un puntero hacia el correcto?)

    – Chris

    19 de julio de 2011 a las 22:23

  • Chris, edité el error tipográfico. En su pregunta original, no necesita preocuparse, siempre llamará a la función polimórfica correcta.

    – KerrekSB

    19 de julio de 2011 a las 22:27

  • @Chris, en el nivel bajo, el puntero de función miembro, para funciones virtuales, contiene un índice vtable, no un puntero de función real. Cuando se invoca el puntero de función, el compilador buscará el índice en vtable y llamará a la función en cuestión.

    – bdonlan

    19 de julio de 2011 a las 22:28

  • Exactamente. Llamar a través de PTMF invoca exactamente la misma maquinaria que una llamada directa a x.func() causaría si x es polimórfico, es decir, una búsqueda en la tabla vtable de la clase de la ubicación más derivada de func.

    – KerrekSB

    19 de julio de 2011 a las 22:30


  • “desafío de sintaxis”, por lo que dado un puntero a una función miembro de clase derivada desconocida, ¿desea llamar a la función miembro de clase base del mismo nombre? No creo que puedas, aunque podría estar equivocado. el punto sobre p->Basil::foo() es que está utilizando un nombre completo para referirse al método de Basil – dado solo un puntero, no conoce el nombre de la función y, por lo tanto, no puede usar ese mismo símbolo en un nombre completamente calificado.

    –Steve Jessop

    19 de julio de 2011 a las 22:49


Puede obtener un puntero hacia él, así:

struct A {
    virtual void foo() = 0;
};

typedef void (A::*LPFOO)();

LPFOO pFoo = &A::foo;

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad