2010-11-17 37 views
8

我已经写以下代码:C++ - 约函数指针问题

#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; 
} 

我已经运行上VS2008上面的代码,并且每个函数调用返回“100”。 这里是问题:

Q1>代码中是否有任何问题?

Q2>似乎C++在* pf和pf之间没有区别。那是对的吗?

谢谢

+1

+1对于一个非常明确的问题。 – 2010-11-17 16:31:59

+0

你也可以用g的声明和g2的body一起使用 – UncleZeiv 2010-11-17 16:33:55

+1

我建议本教程关于函数指针:http://www.newty.de/fpt/index.html ..它非常全面 – Jack 2010-11-17 16:34:36

回答

8

C++ 确实实际上区分了double()double(*)()两种类型,但区别很微妙。当您将函数类型作为参数传递给函数时,函数类型会自动“降级”为函数指针。 (这是类似的,我想,对数组类型如何降解为当作为函数参数传递的指针类型。)

然而,功能类型和函数指针型仍然不同类型,根据C++类型系统。考虑以下情况:

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(); 
} 

由于无法将函数类型声明为自动变量,因此应该编译失败。 (请记住,函数不是C++中的第一类对象。)所以声明F func;是无效的。然而,如果我们改变我们main功能,使用函数指针代替实例化模板,像这样:

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

...现在它编译。

+3

+1为堆叠式的助手提供帮助,这让我内心的C++程序员微笑。 – 2010-11-17 16:49:53

+0

所以它有点像引用和指针之间的区别? – joshperry 2010-11-19 01:21:02

+0

请注意,如果您要执行typedef void F(),那么'F func'会有效。 struct A {F func; };(而类定义将等同于'struct A {void func();};')。它会在代码中导致错误,因为'F'是依赖的,并且禁止使用依赖类型声明一个不使用函数声明符('(params)'形式)的函数(approp。文本是14.3 0.1/3)。 – 2010-11-19 15:42:21

2

以下功能是相同的:

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

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

解引用一个函数指针(如图克)是一样的调用该函数的名字。

Q2>似乎C++不会在* pf和pf之间产生 差异。那 是否正确?

* pf和pf(作为变量)是有区别的。如果pf是一个函数,* pf和pf()是相同的(注意括号)。

+0

+1是正确的(与此处的几个答案不同) – 2010-11-17 16:52:25

0

对于大多数现代编译器,“(* variable)”没有区别。和“变 - >”。但是,必须检查正在使用的类是否覆盖解除引用操作符。

许多程序员在定义函数指针时使用typedef,主要是为了使读取更容易。此外,double pf()语法可能易于出现可读性错误,并且可能会在执行参数行上的函数时感到困惑。

+0

解除引用运算符是一个无关的错误。此外,你所说的是不真实的。无论是否(* a).b'等于'a-> b''与编译器没有任何关系 - 这只是运算符是否适合于'a'类型的过载问题。 – 2010-11-17 16:41:42

0

您发布的代码没有问题或不同。但是,如果您正在编写采用函数的模板,则应该使用g2中的语法。考虑以下几点:

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

需要注意的是,如果你functor之前把引用操作,你限制你写的函数指针算法的效用。然而,如果你不把它放在那里,有人可以传递一个STL函子,比如std::bind2nd返回的东西。

因此,我总体上建议在可能的情况下使用第二个(无*)语法。

0

考虑下面的代码段

void pf(); 

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

在另一方面

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

所以存在从一个指针的函数(其被称为“衰减”)的隐式转换。这也使得你可以说***...***pf--任意多次解引用它 - 在每一步中都会发生一个指针转换函数,这个函数可以解除之前的解除引用的影响。

在函数的参数列表中,T f()T (*f)()是声明一个参数

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

的等同的方式(除了拼写)的引用将抑制该参数类型调整

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

这是与数组声明的参数完全相同:参数从来都不是数组,但它们总是指针,如果它们被声明为数组。