funcion - puntero a clase c++
¿No se permiten typedef no puntero de funciones miembro? (3)
Para funciones que no son miembros, un tipo como typedef void(Function)()
tiene varios usos, pero para funciones miembro la única aplicación es declarar una variable que contiene un puntero a función. Por lo tanto, aparte de una preferencia estilística, no hay una necesidad estricta de permitir esta sintaxis y se ha omitido de la norma.
Fondo
El ::
es un operador de resolución de alcance, y la sintaxis X::Y
está reservada para static
acceso de miembro static
si X
es un tipo de clase. Así que X::*Z
fue otra sintaxis inventada para definir puntero a miembro .
Olvídese de la función miembro por un tiempo, solo piense en los datos de miembros y vea este código:
struct X
{
int a;
};
int X::*pa = &X::a; //pointer-to-member
X x = {100}; //a = 100
cout << (x.*pa) << endl;
Define los datos de puntero a miembro , y el cout
usa para imprimir el valor de a
objeto x
, e imprime:
100
Demostración: http://www.ideone.com/De2H1
Ahora piense, si a X::pa
(a diferencia de X::*pa
) se les permitió hacer eso, entonces ha escrito lo anterior como:
int X::pa = X::a; //not &X::a
Al ver esta sintaxis, ¿cómo diría si X::a
es un miembro static
o no estático? Esa es una de las razones por las que el Estándar creó una sintaxis de puntero a miembro , y la aplica de manera uniforme a los datos de miembro no estáticos, así como a la función de miembro no estática .
De hecho, no puede escribir X::a
, tiene que escribir &X::a
. La sintaxis X::a
resultaría en un error de compilación (ver this ).
Ahora extienda este argumento de miembro-datos a miembro-función . Supongamos que tienes un typedef definido como:
typedef void fun();
Entonces, ¿qué crees que hace el siguiente código?
struct X
{
fun a;
};
Bueno, define el miembro a
de tipo fun
(que es una función que no tiene argumentos y devuelve un vacío), y es equivalente a esto:
struct X
{
void a();
};
¿Sorprendido? Sigue leyendo
struct X
{
fun a; //equivalent to this: void a();
};
void X::a() //yes, you can do this!
{
cout << "haha" << endl;
}
Podemos usar exactamente la misma sintaxis para referirnos a a
que ahora es una función miembro:
X x;
x.a(); //normal function call
void (X::*pa)() = &X::a; //pointer-to-member
(x.*pa)(); //using pointer-to-member
La similitud es el sinatax en el lado derecho: &X::a
. Si se refiere a una función miembro o datos de miembros, la sintaxis es la misma.
Demostración: http://www.ideone.com/Y80Mf
Conclusión:
Como sabemos, no podemos escribir X::a
en RHS, sin importar si a
es un miembro-datos o una función-miembro. La única sintaxis que se permite es &X::f
que hace necesario que el tipo de destino (en LHS) también sea un puntero , lo que a su vez hace que la sintaxis sea void (X::*pa)()
absolutamente necesaria y fundamental. ya que encaja con otra sintaxis en el lenguaje.
Después de obtener una respuesta a esta pregunta , descubrí que hay dos formas válidas de escribir un puntero a una función.
typedef void (Function) ();
typedef void (*PFunction) ();
void foo () {}
Function * p = foo;
PFunction q = foo;
Ahora prefiero Function * p
a PFunction q
pero aparentemente esto no funciona para las funciones de puntero a miembro. Considera este ejemplo artificial.
#include <iostream>
struct Base {
typedef void (Base :: *Callback) ();
//^^^ remove this ''*'' and put it below (i.e. *cb)
Callback cb;
void go () {
(this->*cb) ();
}
virtual void x () = 0;
Base () {
cb = &Base::x;
}
};
struct D1 : public Base {
void x () {
std :: cout << "D1/n";
}
};
struct D2 : public Base {
void x () {
std :: cout << "D2/n";
}
};
int main () {
D1 d1;
D2 d2;
d1 .go ();
d2 .go ();
}
Pero si lo cambio al nuevo estilo preferido: typedef void (Base :: Callback) ()
y Callback * cb
, obtengo un error del compilador en el punto de typedef
Calificación adicional ''Base ::'' en el miembro ''Devolución de llamada''
¿Por qué esto no está permitido? ¿Es simplemente un descuido o causaría problemas?
Para ser precisos, los dos typedef''s en el caso de los punteros que no son miembros no son los mismos:
typedef void function();
typedef void (*fptr)();
La primera define la function
como una función que no toma argumentos y devuelve el void
, mientras que la segunda define ftpr
como un puntero a la función que no tiene argumentos y devuelve el void
. La confusión probablemente surja ya que el tipo de función se convertirá implícitamente en un tipo de puntero en muchos contextos. Pero no todos:
function f; // declares void f();
struct test {
function f; // declares void test::f()
};
void g( function f ); // declares g( void (*f)() ): function decays to pointer to function in declaration
g( f ); // calls g( &f ): function decays to pointer to function
void f() {} // definition of f
// function h = f; // error: cannot assign functions
function *h = f; // f decays to &f
Vamos a omitir la parte de "función" por un segundo. En C ++, tenemos los tipos int
, int*
y int Foo::*
. Es un entero normal, un puntero a entero y un puntero a un miembro entero. No hay un cuarto tipo "miembro entero".
Exactamente lo mismo se aplica a las funciones: simplemente no hay ningún tipo "función miembro", aunque hay tipos de función, tipos de puntero de función y tipos de puntero de función de miembro.