2010-11-17 24 views
8

he escrito el siguiente código:C++ - Preguntas sobre los punteros de función

#include "stdafx.h" 
#include <iostream> 
using namespace std; 

double funcA() 
{ 
    return 100.0; 
} 

int g(double (*pf)()) 
{ 
    cout << (*pf)() << endl; 
    return 0; 
} 

int g2(double pf()) 
{ 
    cout << pf() << endl; 
    return 0; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    g(&funcA); // case I 
    g(funcA); // case II 

    g2(funcA); // case III 
    g2(&funcA); // case IV 
    return 0; 
} 

he ejecutar el código anterior en VS2008 y cada llamada a la función devuelve '100'. Aquí está la pregunta:

Q1> ¿Hay algún problema en el código?

Q2> Parece que C++ no hace la diferencia entre * pf y pf. ¿Es eso correcto?

Gracias

+1

+1 para una pregunta muy clara. –

+0

también puede usar la declaración de g con el cuerpo de g2 – UncleZeiv

+1

Sugiero este tutorial sobre punteros de función: http://www.newty.de/fpt/index.html .. es muy completo – Jack

Respuesta

8

C++ hace, de hecho, hacer una distinción entre los tipos double() y double(*)(), pero la diferencia es sutil. Cuando pasa un tipo de función como argumento a una función, el tipo de función se "degrada automáticamente" a un puntero de función. (Esto es similar, supongo, a la forma en un tipo de matriz se degrada a un tipo de puntero cuando pasa como un argumento de la función.)

Sin embargo, un tipo de función y un tipo de función-puntero son todavía diferentes tipos, de acuerdo con el sistema de tipos C++. Considere el siguiente caso:

void g() { } 

template <class F> 
struct Foo 
{ 
    Foo(const F& f) : func(f) 
    { } 

    void operator()() { func(); } 

    F func; 
}; 


int main() 
{ 
    Foo<void()> f(g); 
    f(); 
} 

Esto debería dejar de compilar, ya que no se puede declarar un tipo de función como una variable automática. (Recuerde, las funciones no son objetos de primera clase en C++). Por lo tanto, la declaración F func; no es válida. Sin embargo, si cambiamos nuestra función main para crear instancias de la plantilla en lugar usando una función de puntero , así:

int main() 
{ 
    typedef void(*function_pointer)(); 
    Foo<function_pointer> f(g); 
    f(); 
} 

... ahora compila.

+3

+1 para ayudar a la pedantería, lo que hace sonreír a mi programador interno de C++. –

+0

¿Es como la diferencia entre una referencia y un puntero? – joshperry

+0

Tenga en cuenta que 'F func' sería válido si tuviera que hacer' typedef void F(); struct A {F func; }; '(y la definición de clase sería equivalente a' struct A {void func();}; '). Causa un error en su código porque 'F' es dependiente, y está prohibido declarar una función que no usa un declarador de función (la forma' (params) ') usando un tipo dependiente (el texto apropiado está en 14.3 .1/3). –

2

Las siguientes funciones son idénticas:

int g(double (*pf)()) 
{ 
    cout << (*pf)() << endl; 
    return 0; 
} 

int g2(double pf()) 
{ 
    cout << pf() << endl; 
    return 0; 
} 

eliminación de referencias a un puntero de función (como se muestra en g) es el mismo que llamar a nombre de esa función.

Q2> Parece que C++ no hace diferencia entre * pf y pf. ¿Eso es correcto?

Hay una diferencia entre * pf y pf (como variables). Si pf es una función, * pf y pf() son idénticos (tenga en cuenta los paréntesis).

+0

+1 para ser correcto (a diferencia de varias respuestas aquí) –

0

Con la mayoría de los compiladores modernos, no hay diferencia entre "(* variable)". y "variable->". Sin embargo, uno debe verificar la clase en uso para ver si anula el operador de desreferencia.

Muchos programadores usan typedef al definir punteros a funciones, principalmente para facilitar la lectura. Además, la sintaxis double pf() puede ser propensa a errores de legibilidad y puede confundirse con la ejecución de una función en la línea de parámetros.

+0

El operador de desreferencia es un error no relacionado. Además, lo que dices es falso. Si '(* a) .b' es igual a' a-> b' no tiene nada que ver con el compilador, solo se trata de si los operadores están sobrecargados adecuadamente para el tipo de 'a'. –

0

No hay ningún problema o diferencia en el código que ha publicado. Sin embargo, si está escribiendo plantillas que toman functors, debe usar la sintaxis en g2.Considere lo siguiente:

template<typename Iter, typename Func> 
void for_each(Iter begin, Iter end, Func functor) 
{ 
    for(; begin != end; ++begin) 
    { 
     functor(*begin); 
    } 
} 

Tenga en cuenta que si se pone el operador antes de eliminar la referencia functor, se limita la utilidad del algoritmo de haber escrito a los punteros de función. Sin embargo, si no lo coloca allí, alguien puede pasar un funtor STL, como algo devuelto por std::bind2nd.

Por lo tanto, en general recomendaría utilizar la segunda (sin *) sintaxis donde sea posible.

0

Considere el siguiente fragmento de código

void pf(); 

void (&prf)() = pf; // OK, bind prf to pf 
void (&prf)() = &pf; // ill-formed, can't bind prf to an function pointer value 

Por otro lado

void (*ppf)() = pf; // OK, function decays to a pointer 
void (*ppf)() = &pf; // OK, pointer assigned to a pointer 

Así que hay una conversión implícita de una función a un puntero (que se llama "la decadencia"). Esto también le permite decir ***...***pf - muchas veces lo desreferencia arbitrariamente - en cada paso se produce una función para la conversión del puntero que deshace el efecto de la desreferencia previa.

En listas de parámetros de función, un T f() y una T (*f)() son formas equivalentes (excepto para la ortografía) de declarar un parámetro

void f(void g()); // g has type void (*)() 
void f(void (*g)()); // g has type void (*)() 

Una referencia inhibirá este ajuste tipo de parámetro

void f(void (&g)()); // g has *not* the type void (*)() 

Este es exactamente lo mismo que para los parámetros de matriz declarada: los parámetros nunca son matrices, pero siempre serán punteros, si se declararon como matrices.