2012-04-08 17 views
8

A diferencia de las funciones miembro virtuales, necesito una solución en la que una función implementada en cada derivación de clase de nivel pueda registrarse para una llamada posterior por la clase base. (No solo la implementación más derivada)Cómo registrar un puntero de función de miembro derivado de clase con una clase base

Para hacer esto, estaba pensando en proporcionar un mecanismo para que las clases derivadas registren su función con la clase base, como durante el constructor de la clase derivada.

No obstante, tengo problemas con el argumento del puntero a la función miembro. Estaba pensando que Derived se deriva de Base, el puntero this se debe convertir automáticamente.

¿Se puede hacer esto cerca de lo que estoy intentando o necesito usar funciones de miembro estáticas, void * y static_cast?

class Base 
{ 
protected: 
    typedef void (Base::*PrepFn)(int n); 
    void registerPrepFn(PrepFn fn) {}; 
} 

class Derived : public Base 
{ 
    Derived() { 
     registerPrepFn(&Derived::derivedPrepFn); 
    }; 

    void derivedPrepFn(int n) {}; 

} 

de error del compilador:

error: no matching function for call to 'Derived::registerPrepFn(void (Derived::*)(int))' 
note: candidates are:     'void Base::registerPrepFn(void (Base::*)(int))' 
+1

No estoy seguro exactamente de lo que quiere hacer. Pero ciertamente no es posible de esta manera; una función miembro definida en 'Derived' no puede ser apuntada por' Base :: * '. Quizás si explica el objetivo de nivel superior, alguien podría sugerir una mejor solución. –

+3

FTR, 'void *' y 'static_cast' tampoco funcionarán, porque un puntero a la función miembro no es un puntero (sí, tiene un nombre horrible). –

+0

con respecto a la conversión estática, estaba pensando que podría implementar funciones de miembro estáticas y explícitamente pasar alrededor de este puntero – NoahR

Respuesta

12

Si todo lo que necesita es vencer el mensaje de error, a continuación, la fundición va a hacer:

class Derived : public Base 
{ 
    Derived() { 
     registerPrepFn(static_cast<PrepFn>(&Derived::derivedPrepFn)); 
    }; 

    void derivedPrepFn(int n) {}; 

} 

Call normalmente con un Base* p (siempre que en realidad apunta a una Derivado): (p->*registered)(0)

Ver http://ideone.com/BB9oy para un ejemplo de trabajo.

+0

bueno, no quiero simplemente suprimir el mensaje de error. ¿Funcionará esto? – NoahR

+0

@NoahR: Sí, funcionará (pero tenga cuidado con la condición en negrita) – jpalecek

+1

El estándar tiene esto que decir en dicha conversión: "Si la clase B contiene el miembro original, o es una clase base o derivada de la clase que contiene el miembro original, el puntero resultante al miembro apunta al miembro original. De lo contrario, el resultado del elenco no está definido ". Me temo que un modelo así no hará lo que el OP quiere en el mejor de los casos, o no produce un valor útil en el peor. –

0

Esto no está permitido con la programación orientada a objetos. El cambio de comportamiento se logra al polimorfizar la clase del objeto en el momento de creación del objeto.

Si necesita cambiar el comportamiento de creación posterior al objeto, puede refactorizar el comportamiento dinámico a otro conjunto de clases polimórficas y mantener un "puntero" a una instancia de una clase con el comportamiento correcto. Por favor, busque el patrón de software de "clase decorada".

Cuestiones relacionadas