17

在得到这个问题的答案后,我发现有两种有效的方法来定义函数指针。

typedef void (Function) ();
typedef void (*PFunction) ();

void foo () {}

Function * p = foo;
PFunction  q = foo;

我现在更喜欢Function * pPFunction q但显然这不适用于指向成员的函数。考虑这个人为的例子。

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

但是,如果我将其更改为新的首选样式:typedef void (Base :: Callback) ()并且Callback * cb,我会在以下位置出现编译器错误typedef

成员“回调”的额外资格“基础::”

错误演示

为什么不允许这样做?这仅仅是一个疏忽还是会导致问题?

4

3 回答 3

10

对于非成员函数,诸如此类的类型typedef void(Function)()有多种用途,但对于成员函数,唯一的应用是声明一个包含函数指针的变量。因此,除了风格偏好之外,没有严格需要允许这种语法,并且它已从标准中省略。

背景

::是范围解析运算符,如果是类类型,则语法X::Y保留用于static成员访问。发明了另一种语法来定义指向成员的指针XX::*Z

暂时忘记member-function,想想member-data,看看这段代码:

struct X
{
   int a;
};

int X::*pa = &X::a; //pointer-to-member
X x = {100}; //a = 100
cout << (x.*pa) << endl;

它定义了一个指向成员数据的指针,并cout使用它来打印aobject的值x,并打印:

100

演示:http ://www.ideone.com/De2H1

现在想一想,如果X::pa(而不是X::*pa)被允许这样做,那么您将上面的内容写为:

int X::pa = X::a; //not &X::a

看到这个语法,你怎么知道X::astatic成员还是非静态成员?这就是标准提出指向成员语法并将其统一应用于非静态成员数据非静态成员函数的原因之一。

事实上,你不能X::a,你必须写&X::a。该语法X::a会导致编译错误(请参阅this)。


现在将member-data的这个参数扩展到member-function。假设你有一个 typedef 定义为:

typedef void fun();

那么你认为下面的代码做了什么?

struct X
{
   fun a;
};

好吧,它定义a了类型的成员fun(它是不带参数的函数,并返回 void),并且等价于:

struct X
{
   void a();
};

惊讶吗?继续阅读。

struct X
{
   fun a; //equivalent to this: void a();
};

void X::a() //yes, you can do this!
{
     cout << "haha" << endl;
}

我们可以使用完全相同的语法来引用a现在的成员函数:

X x;
x.a(); //normal function call

void (X::*pa)() = &X::a; //pointer-to-member
(x.*pa)(); //using pointer-to-member

相似之处是右侧的语法:&X::a。无论a是指成员函数还是成员数据,语法都是一样的。

演示:http ://www.ideone.com/Y80Mf

结论:

我们知道我们不能X::a在 RHS 上写,不管a是成员数据还是成员函数。唯一允许的语法是&X::f使得目标类型(在 LHS 上)也必须是指针,这反过来又使语法void (X::*pa)()绝对必要和基本,因为它适合语言中的其他语法。

于 2011-09-15T11:00:31.203 回答
2

准确地说,在非成员指针的情况下,两个 typedef 是不一样的:

typedef void function();
typedef void (*fptr)();

第一个定义function不带参数并返回的函数void,而第二个定义ftpr指向不带参数并返回的函数void的指针。由于函数类型将在许多上下文中隐式转换为指针类型,因此可能会出现混淆。但不是所有的:

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
于 2011-09-15T11:08:10.157 回答
0

让我们暂时跳过“功能”部分。在 C++ 中,我们有int、 theint*int Foo::*类型。这是一个常规整数、指向整数的指针和指向整数成员的指针。没有第四种“整数成员”。

完全相同的情况也适用于函数:只是没有类型“成员函数”,尽管有函数类型、函数指针类型和成员函数指针类型。

于 2011-09-15T12:12:28.957 回答