指向成员函数的函数指针

Posted

技术标签:

【中文标题】指向成员函数的函数指针【英文标题】:Function pointers to member functions 【发布时间】:2010-04-30 16:00:29 【问题描述】:

这有几个重复,但没有人解释为什么我可以使用成员变量来存储指针(在 FOO 中)但是当我尝试使用局部变量时(在 BAR 的注释部分),它是非法的。有人能解释一下吗?

#include <iostream>
using namespace std;

class FOO

public:
 int (FOO::*fptr)(int a, int b);
 int add_stuff(int a, int b)
 
  return a+b;
 
 void call_adder(int a, int b)
   
  fptr = &FOO::add_stuff;
  cout<<(this->*fptr)(a,b)<<endl;
 
;

class BAR

public:
 int add_stuff(int a, int b)
 
  return a+b;
 
 void call_adder(int a, int b)
   
  //int (BAR::*fptr)(int a, int b);
  //fptr = &BAR::add_stuff;
  //cout<<(*fptr)(a,b)<<endl;
 
;

int main()

 FOO test;
 test.call_adder(10,20);
 return 0;

【问题讨论】:

为了避免与真正的函数指针或“函数指针”类型的成员变量混淆,我避免调用指向成员函数的指针,“成员函数指针”。在许多方面,指向成员函数的指针与指向数据成员的指针的共同点比与函数指针的共同点要多。 【参考方案1】:

显然,您在FOO的调用中误解了this-&gt;*的含义。

当您将this-&gt;* 与成员fptr 指针一起使用时,this-&gt;* 部分与fptrFOO 的成员完全无关。当您使用指向成员的指针调用成员函数时,您必须使用 -&gt;* 运算符(或 .* 运算符)并且您总是必须指定要使用的实际对象用那个指向成员的指针。这就是调用表达式的this-&gt;* 部分所做的。 IE。调用将总是看起来像

(<pointer-to-object> ->* <pointer-to-member>) (<arguments>)

或作为

(<object> .* <pointer-to-member>) (<arguments>)

不能省略调用的左侧(&lt;pointer-to-object&gt;&lt;object&gt; 以上)。

换句话说,不管fptr 是成员变量、局部变量、全局变量还是任何其他类型的变量,通过fptr 的调用将总是看起来像

(this->*fptr)(a, b);

假设您想使用*this 对象调用它。再举一个例子,如果你想为指针pfoo指向的其他对象调用它,调用将如下所示

FOO *pfoo;
...
(pfoo->*fptr)(a, b);

在您的 BAR 类中,调用应该看起来像 (this-&gt;*fptr)(a,b),即使 fptr 是一个局部变量。

【讨论】:

我的印象是类的 all 对象会使用具有 same 地址的函数 --- 那么我们为什么需要 @ 987654344@当函数地址与对象无关时---还是我弄错了? @Jacob:您是正确的,所有函数都使用相同的地址,但是如果该成员函数修改它的对象,它必须知道需要修改相关类的哪个实例。这是通过this 指针实现的,这就是为什么在使用函数指针之前实际上需要一个类的实例。 +1 澄清我的误解,感谢您的解释! @Jacob:首先,你不能只调用一个非静态成员函数,即使你知道它的确切地址。知道地址是不够的。每个非静态成员函数都有一个隐式参数this。这个参数需要一个参数。这正是您在-&gt;* 之前提供的内容:隐式this 参数的参数。其次,如果函数是虚函数,则指针不附加到虚函数的特定版本。调用仍将通过 VMT 解决,为此我们需要知道对象。【参考方案2】:

当你使用成员函数指针时,你需要指定它所作用的对象。

即您需要创建一个指向 BAR 实例的指针(我们称之为 bar)并执行以下操作:

(bar->*fptr)(a,b)

调用函数或 BAR 的实例并执行:

(bar.*fptr)(a,b)

换一种说法:

#include <iostream>

class BAR

    int i;
public:
    BAR(): i(0) ;
    int AddOne()  return ++i; ;
    int GetI()  return i; ;


int main()

    BAR bar;
    auto fPtr = &BAR::AddOne; // This line is C++0x only (because of auto)
    std::cout << (bar.*fPtr)(); //This will print 1 to the console
    std::cout << std::endl;
    std::cout << bar.GetI(); //This will also print 1 to the console.

【讨论】:

这是他需要创建的 BAR 实例,而不是 FOO。 @Billy ONeal:(foo-&gt;*fptr)(a,b) 中的大括号是绝对必要的。你现在所拥有的根本无法编译。 为什么需要指定对象?同一个类的不同对象的函数地址不一样吗? @Fyodor 和@AndreyT:抱歉——不要经常使用成员函数指针(std::mem_fun_ref 除外)。 @Jacob:因为如果它是一个成员函数,该函数需要接收一个this 指针。如果您的函数不需要实际修改任何对象,则将其设为静态函数,它不需要 this 指针。如果你的函数修改了它所属的对象,它怎么知道要修改哪个类的具体实例? :) @Billy ONeal: bar 在您的-&gt;* 示例中必须是指向类实例的指针,而不是实例本身。【参考方案3】:

我不认为变量本身的使用是非法的。尝试在没有类实例的情况下调用该方法是非法的。

也就是说,你真的应该调用(someVar-&gt;*fptr)(a,b),其中someVar 的类型是BAR*

【讨论】:

谢谢,但我以为所有对象的成员函数地址都是一样的---还是我弄错了? @Fyodor Soikin:+1 用于修复我的答案中的错误。 是的,所有对象都一样。因此,您不需要对象来获取该地址。但是您确实需要一个对象来通过该地址调用函数。成员函数需要一个“this”指针——换句话说,它需要一个可以调用它的对象。【参考方案4】:

BAR::call_adder() 有几个问题。一方面,你在混合案例。在 C++ 中,大小写很重要。 BARbar 不一样。其次,在解决案例问题后,您贴标并分配了指针,但是当您尝试通过指向成员函数的指针调用时,您需要将operator -&gt;* 与类对象一起使用。这是call_adder() 固定的

 void call_adder(int a, int b)
   
  int (BAR::*fptr)(int a, int b);
  fptr = &BAR::add_stuff;
  cout<<(this->*fptr)(a,b)<<endl;
 

【讨论】:

我不明白 --- 我在哪里使用 bar 而不是 BAR?感谢您的解决方案,但为什么我需要将 -&gt;* 与类对象一起使用?【参考方案5】:

当您调用类的成员函数时,编译器会生成代码以在函数运行时设置“this”。当您从未完成的函数指针调用它时。有一些方法可以解决它,但它们不能“保证”工作并且依赖于编译器。只要您小心并知道可能遇到的问题,您就可以做到。

【讨论】:

以上是关于指向成员函数的函数指针的主要内容,如果未能解决你的问题,请参考以下文章

c++怎样将指向【成员函数】的指针强转成指向【普通函数】的指针

从 C++ 的成员函数中获取指向成员函数的指针

如何从静态成员函数调用指向成员函数的指针?

指向函数对象的成员函数指针

C++指向对象成员函数的指针

C++指向对象成员函数的指针