C++ -- 函数指针相关问题

7

我已经编写了以下代码:

#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 对于一个非常清晰的问题。 - Paul Sonier
你可以使用 g 的声明和 g2 的主体。 - UncleZeiv
1
我建议这个关于函数指针的教程:http://www.newty.de/fpt/index.html。它非常详尽。 - Jack
+1 -- 但是 using namespace std; 让我毛骨悚然 ;) - Billy ONeal
@Jack,感谢你的教程。 - q0987
5个回答

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++ 程序员微笑。 - Billy ONeal
这有点像引用和指针之间的区别吗? - joshperry
请注意,如果您执行了 typedef void F(); struct A { F func; };,那么 F func 将是有效的(并且类定义将等同于 struct A { void func(); };)。但是,在您的代码中它会导致错误,因为 F 是依赖类型,并且禁止使用依赖类型(适当的文本在 14.3.1/3)声明不使用函数声明符号((params)形式)的函数。 - Johannes Schaub - litb

2
以下函数是相同的:
int g(double (*pf)())
{
    cout << (*pf)() << endl;
    return 0;
}

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

对函数指针进行解引用(如g所示)等同于调用该函数的名称。

Q2> 看起来C++不区分*pf和pf,这个正确吗?

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


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 (*)()

这与声明数组参数完全相同:参数永远不是数组,但如果它们被声明为数组,则始终将它们作为指针。


0

在大多数现代编译器中,“(*variable)”和“variable->”之间没有区别。但是,必须检查正在使用的类以查看它是否覆盖了解引用运算符。

许多程序员在定义函数指针时使用typedef,主要是为了使阅读更容易。此外,“double pf()”语法可能容易出现可读性错误,并且可能会与参数行上执行函数混淆。


解引用运算符是一个无关的错误。此外,你所说的是不正确的。(*a).b 是否等于 a->b 与编译器无关 - 这只是一个问题,取决于是否适当地为类型 a 进行了重载。 - Konrad Rudolph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接