一般来说,函数指针的用法是比较简单的。 比如对于函数:

1
int innerFunc(int num);

可以使用:

1
2
int (*ptrFunc)(int);
ptrFunc = innerFunc;//或&innerFunc

或者为了复用:

1
2
3
typedef int (*FUNC)(int);
FUNC ptrFunc;
ptrFunc = innerFunc;//或&innerFunc

但是当你使用类成员函数指针的时候,会发现完全不是那么一回事!而我今天就杯具的遇上了。。
废话不多说,直接上代码吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <string>
#include <vector>
#include <map>
using namespace std;
 
class Foo
{
    public:
        string m_str;
        Foo()
        {
            m_str = "";
        }
        void test(char* str,int num)
        {
            m_str = str;
            int (Foo::*ptrFunc)(int);
 
            ptrFunc = &Foo::innerFunc;
            //(*ptrFunc)(num);
            //(this->(*ptrFunc))(num);
            (this->*ptrFunc)(num);
        }
        int innerFunc(int num)
        {
            cout<<num<<endl;
            cout<<m_str<<endl;
        }
};
 
int main(int argc, const char *argv[])
{
    Foo foo;
    foo.test("woo",100);
    return 0;
}

注释的部分千万不要打开,也千万不要以为是一样的,如果你不听我的劝阻非要试一下,好吧,他们会分别报如下错误:

error: invalid use of `unary *' on pointer to member

error: expected unqualified-id before '(' token
error: invalid use of `unary *' on pointer to member

对于原因现在也没时间去钻研了,如果哪位朋友知晓麻烦告知一下,多谢~





原创文章,版权所有。转载请注明:转载自Vimer的程序世界 [ http://www.vimer.cn ]

本文链接地址: http://www.vimer.cn/?p=1668

21 个评论 在 “关于类成员函数指针的正确写法”

  1. narky 说:

    不用想太多
    ->* 就是固定的调用成员函数指针的操作符

    看这里
    http://www.builder.com.cn/2008/0327/785349.shtml

    [回复]

    Dante 回复:

    被系统当作垃圾评论了。。。才看到。。
    这篇文章在CSDN上看到过,呵呵,我也觉得当作固定用法得了,深究太累。。。

    [回复]

  2. Marshall 说:

    我觉得可能因为成员函数有默认this参数的原因吧

    [回复]

    Dante 回复:

    嗯,大体看了一下介绍,好像这种成员函数的函数指针不是4个字节,然后就没有再深究了。。

    [回复]

  3. 瑞士菜刀 说:

    int (Foo::*ptrFunc)(int);
    之所以成员函数的指针要这么写,是因为成员函数需要隐式提供指向被调对象的指针(即成员函数内部的 this 指针)。

    foo.innerFunc();
    由「.」操作符指明将 foo 的地址作为 this 指针。

    pFoo->innerFunc();
    由「->」操作符指明将 pFoo 作为 this 指针。

    (*ptrFunc)(num);
    无法为 ptrFunc 提供隐式的 this 指针,所以是非法调用。

    (this->(*ptrFunc))(num);
    由于括号的介入,先解析指针,再选择成员。本质上和前一种情况是一样的。

    (this->*ptrFunc)(num);
    -> 优先级大于 *,于是先选择成员,再解析指针。由于已经由「->」操作符指定了成员,所以 ptrFunc 可以被隐式提供一个指针。

    [回复]

    Dante 回复:

    居然被系统当作垃圾评论,看来过滤器有问题。。。
    瑞士菜刀说的很详细,不过还是有一点不太理解,能否解释一下选择成员的意思呢?

    [回复]

  4. 瑞士菜刀 说:

    int (Foo::*ptrFunc)(int);
    成员函数指针之所以一定要加上「Foo::」前缀,是因为 Foo 的非静态成员函数要求调用时隐式传入一个 Foo *const this 指针,用来指明被调对象。

    foo.innerFunc();
    pFoo->innerFunc();
    一般调用时「.」和「->」操作符表明用 &foo 和 pFoo 来作为 this 指针传入。

    (*ptrFunc)(num);
    错误在于「*」操作符解析指针时无法提供隐式的 this 指针,于是这是一个无效调用。

    (this->(*ptrFunc))(num);
    错误在于括号的加入改变了优先级,造成先解析指针,再指定成员归属。本质上和前一种情况时一样的,也是缺少 this 指针的无效调用。

    (this->*ptrFunc)(num);
    「->」运算符的优先级大于「*」,所以先指定成员归属,再解析指针。于是在解析指针时可以知道这个指针属于 *this 对象,故将其作为 ptrFunc 的隐式指针传入。

    [回复]

  5. lichray 说:

    试一下 BSD C 的经典写法就知道错在哪儿了:
    ptrFunc(num);

    error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘ptrFunc (…)’

    [回复]

    Dante 回复:

    ->*居然是个操作符。。。
    C++的语法太复杂了。。

    [回复]

    瑞士菜刀 回复:

    不是的,按照运算符优先级,->比*优先。于是先确定成员归属,之后 * 解析指针的时候由于已经确定了成员归属,可以在解析的时候提供 this 指针,所以成功。

    this->(*p)的做法是先解析指针,后确定归属。解析指针的时候没法提供 this 指针。

    [回复]

    依云 回复:

    呵呵,所以尽管有强大的STL,我还是不喜欢C++。

    [回复]

  6. 瑞士菜刀 说:

    成员函数指针写成 int (Foo::*ptrFunc)(int); 是因为调用 ptrFunc 需要隐式提供一个 Foo *const this 指针。平时调用时由于 . 和 -> 确定了成员函数的归属,于是编译器可以提供这个指针。

    (*ptrFunc)(num); 的做法显然没法提供对象指针,所以是非法调用。

    (this->(*ptrFunc))(num); 由于括号的作用,先解析指针,后确定成员归属。解析指针的时候无法提供对象指针,本质上是和前一种做法一样的。

    (this->*ptrFunc)(num); 由于运算符优先级 -> 大于 * 运算符,先确定成员归属,后解析指针。在解析指针时由于已经确定了归属,可以用 this 当作对象指针隐式传入 ptfFunc,所以调用有效。

    可见 .* 和 ->* 不是操作符。

    [回复]

  7. 瑞士菜刀 说:

    成员函数指针写成 int (Foo::*ptrFunc)(int); 是因为调用 ptrFunc 需要隐式提供一个 Foo *const this 指针。平时调用时由于 . 和 -> 确定了成员函数的归属,于是编译器可以提供这个指针。

    (*ptrFunc)(num); 的做法显然没法提供对象指针,所以是非法调用。

    (this->(*ptrFunc))(num); 由于括号的作用,先解析指针,后确定成员归属。解析指针的时候无法提供对象指针,本质上是和前一种做法一样的。

    (this->*ptrFunc)(num); 由于运算符优先级 -> 大于 * 运算符,先确定成员归属,后解析指针。在解析指针时由于已经确定了归属,可以用 this 当作对象指针隐式传入 ptfFunc,所以调用有效。

    可见 .* 和 ->* 不是操作符。

    [回复]

  8. Terrence 说:

    我的理解是这样的:

    innerFunc是在Foo的域里面的,所以要想调用innerFunc,就要用 作用域::函数名(this->*ptrFunc) 的方法来调用。类似的功能可以通过使用承继类的父类指针来做。

    同理 如果把innerFunc改成static,那么就不需要加域作用空间了。

    [回复]

    Dante 回复:

    对,关于this应该是这样理解~
    只是不明白->*这个操作符是怎么回事,this->*ptrFunc不能写成this->(*ptrFunc)。
    能否指教一下?

    [回复]

    Terrence 回复:

    我认为这里是把编译的一些细节带到了代码编写里面来了。
    函数在编译器中是通过指针来保存的(C和C++最终都变成了汇编),书写代码时不需要考虑这种情况。在函数指针的情况下,
    可以把*ptrFunc当成是innerFunction的代称,调用innerFunction的时候,完整的写法是this->innerFunctino(),也可以直接写innerFunction(编译器给处理了)。所以就有了this->*ptrFunc,同样this->(innerFunction)或this->(*ptrFunc)都是不对的。
    我的理解就是这样子的,再深入的就要找了解编译器结构的人来解释了。

    [回复]

  9. 渡水的疯子 说:

    多谢解释,我也学习了!

    [回复]

  10. sw 说:

    ->*和.*是两个很不常见的C++的操作符…………

    [回复]

    Dante 回复:

    的确很不常见。。。。。

    [回复]

  11. Mr.Bear 说:

    那里写成
    ((*this).*ptrFunc)(num);
    的话,编译也能通过。
    (当然,还没见过这么用this的。。。)

    就如瑞士菜刀说的,这里就是个先解析谁的问题。

    同样的*操作符的情况下,先解析this就没问题;先解析ptrFunc了,就不行了。

    ->优先级比*高还是第一次意识到,向瑞士菜刀学习了!

    [回复]

我要评论

*

*