2009-07-30 22 views
19

Quiero llamar a la implementación de la clase base de una función virtual utilizando un puntero de función miembro.Llamando a la definición de la clase base de la función de miembro virtual con el puntero de función

class Base { 
public: 
    virtual void func() { cout << "base" << endl; } 
}; 

class Derived: public Base { 
public: 
    void func() { cout << "derived" << endl; } 

    void callFunc() 
    { 
     void (Base::*fp)() = &Base::func; 
     (this->*fp)(); // Derived::func will be called. 
         // In my application I store the pointer for later use, 
         // so I can't simply do Base::func(). 
    } 
}; 

En el código anterior, se llamará a la implementación de clase derivada de func desde callFunc. ¿Hay alguna manera de guardar un puntero de función miembro que apunte a Base :: func, o tendré que usar using de alguna manera?

En mi aplicación real utilizo boost :: bind para crear un objeto boost :: function en callFunc que luego utilizaré para llamar a func desde otra parte de mi programa. Entonces, si la función boost :: bind o boost :: tiene alguna forma de evitar este problema, eso también ayudaría.

+0

Posible duplicado de [C++: ¿Puntero a la versión monomórfica de la función de miembro virtual?] (Https://stackoverflow.com/questions/5064614/c-pointer-to-monomorphic-version-of-virtual-member-function) –

Respuesta

11

Cuando llama a un método virtual a través de una referencia o un puntero, siempre activará el mecanismo de llamada virtual que encuentre el tipo más derivado.

Su mejor opción es agregar una función alternativa que no sea virtual.

0

¿Hay alguna razón específica para hacer esto a través de un puntero de función?

Usted debe ser capaz de simplemente escribir:

Base::func(); 

para llamar a la implementación de la clase base.

+1

Como escribí en mi pregunta, guardo el puntero en callFunc pero lo uso para llamar realmente a func desde otro lugar de mi programa. –

+0

Para una función virtual esto no funcionará. Siempre realizará una búsqueda vtable y llamará al método en la clase derivada. – Joel

1

Su problema es que un puntero de función miembro no es exactamente lo mismo que un puntero de función desnuda. En realidad, no es solo un puntero, sino un considerably more complex structure, que varía en sus detalles a nivel de la implementación del compilador. Cuando lo invoca a través de la sintaxis (this->*fp)(), en realidad lo llama al objeto original, lo que provoca el despacho virtual de funciones.

Una cosa que podría funcionar es convertirlo a un tipo de puntero que no sea de método. Esto es un poco chirriante, pero creo que debería funcionar. Todavía es necesario pasar una Base * pero lo hace de forma explícita y el envío función virtual es anulado:

typedef void BasePointer(Base*); 

void callFunc() 
{ 
    BasePointer fp = (BasePointer *)&Base::func; 
    fp(this); 
} 

Actualización: Ok, no, usted no puede hacerlo de esa manera. Es ilegal, y no sería seguro si fuera legal. El C++ FAQ tiene more on this. Pero saber eso no resuelve tu problema. El problema es que, puntero a objeto o puntero a miembro si desea llamar al Base::func a través de un puntero Base, el objeto al que apunta debe también sea un Base. Si puede organizar eso, puede usar un puntero de función miembro.

Aquí hay otro pensamiento, no es bonito, pero al menos funciona. Proporcione una función en Derived, no virtual, que explícitamente llame al Base::func. Señale eso en su lugar. No se escalará si necesita hacer esto en el caso general de muchas variantes diferentes de func y callFunc, pero funcionará bien para un método.

+1

¡Definitivamente eso no funcionará! Compare 'sizeof (& Base :: func)' con 'sizeof (BasePointer)': o –

+0

mmutz: Sí. Intento probar siempre antes de publicar, y hoy no me tomé el tiempo para hacerlo. Era * posible * un 'static_cast' podría ser lo suficientemente inteligente como para sacar la parte del puntero de la función del puntero de la función miembro, pero me temo que mi noción de Python de una función ahora está anulando mi C++ :). – quark

+0

Esto es falso: "lleva consigo no solo la dirección de la función para llamar sino también el objeto para llamarlo" –

0

Además de lo que dice el quark, una observación más general es que debe utilizar una implementación de señal/ranura en lugar de un puntero de función simple. Boost tiene uno, hay libsigc y muchos más.

2

Desafortunadamente, lo que intenta hacer no es posible. Las funciones de puntero a miembro son diseñadas para mantener la virtualidad de la función apuntada.

0

¿Qué pasa con esto?

(Base(*this).*fp)(); 

Ahora bien, si usted está satisfecho con eso, se plantea la cuestión de por qué estás incluso utilizando un puntero de función en el primer lugar. Creo que un contexto más podría ayudar.

+0

Esto ya fue sugerido por quark ("... si desea llamar Base :: func a través de un puntero Base, el objeto al que apunta también debe ser una Base"). Asumo que Eddie no puede garantizar que sepa el tipo al que cortar cuando llama a la función. – Troubadour

Cuestiones relacionadas