views:

251

answers:

5

There are several duplicates of this but nobody explains why I can use a member variable to store the pointer (in FOO) but when I try it with a local variable (in the commented portion of BAR), it's illegal. Could anybody explain this?

#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;
}
+4  A: 

When you use a member function pointer, you need to specify the object on which it is acting.

I.e. you need to create a pointer to an instance of BAR (let's call it bar) and do:

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

to call the function, or an instance of BAR and do:

(bar.*fptr)(a,b)

Put another way:

#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.
}
Billy ONeal
It's an instance of BAR he needs to create, not FOO.
Fyodor Soikin
@Billy ONeal: The braces in `(foo->*fptr)(a,b)` are absolutely necessary. What you have now simply won't compile.
AndreyT
Why do I need to specify the object? Don't different objects of the same class have the same function address?
Jacob
@Fyodor and @AndreyT: Sorry -- don't use member function pointers often (Except in std::mem_fun_ref). @Jacob: Because if it's a member function, the function needs to receive a `this` pointer. If your function does not need to actually modify any object, than make it a static function, which does not require the `this` pointer. If your function modifies the object of which it is a member, how does it know which specific instance of the class to modify? :)
Billy ONeal
Thanks! That makes sense. In your answer, I think you meant to use `this` instead of `bar`?
Jacob
@Jacob: That would work as well -- though actually I did mean bar. Things outside of the class can use that function pointer to -- it's not restricted to using `this`. For example, see the `std::mem_fun` and `std::mem_fun_ref` adapters' typical use in STL algorithms.
Billy ONeal
Could you define `bar` for me?
Jacob
@Jacob: `bar` is an instance of class `BAR`.
Billy ONeal
Got it! Thanks for the explanation.
Jacob
@Jacob: Thanks for the checkmark. :) I've edited in an example illustrating use of a member function pointer outside of the class.
Billy ONeal
@Billy ONeal: `bar` in your `->*` example must be a *pointer* to an instance of the class, not the instance itself.
AndreyT
@AndreyT: Yes, I am an idiot.
Billy ONeal
+2  A: 

I don't think the usage of the variable itself is illegal. What's illegal is trying to call that method without a class instance.

That is, you should really call (someVar->*fptr)(a,b) where someVar is of type BAR*

Fyodor Soikin
Thanks, but I thought that the member function address is the same for all objects --- or am I mistaken?
Jacob
@Fyodor Soikin: +1 for fixing the mistake in my answer.
Billy ONeal
Yes, it is same for all objects. Therefore, you do not need an object to get that address. But you do need an object to CALL the function by that address. The member function needs a "this" pointer - in other words, it needs an object on which it gets called.
Fyodor Soikin
+1  A: 

BAR::call_adder() had a couple of problems. For one, you were mixing case. In C++, case is signifigant. BAR and bar are not the same. Second, you decalred and assigned the pointer fine, after fixing the case problems, but when you try to call through the pointer to a member function, you need to use operator ->* with a class object. Here's is call_adder() fixed

 void call_adder(int a, int b)
 {  
  int (BAR::*fptr)(int a, int b);
  fptr = &BAR::add_stuff;
  cout<<(this->*fptr)(a,b)<<endl;
 }
John Dibling
I don't understand --- where did I use `bar` instead of `BAR`? Thanks for the solution, but why I do need to use `->*` with the class object?
Jacob
+8  A: 

Apparently, you misunderstand the meaning of this->* in the call in FOO.

When you use this->* with the member fptr pointer, the this->* part has absolutely nothing to do with fptr being a member of FOO. When you call a member function using a pointer-to-member, you have to use the ->* operator (or .* operator) and you always have to specify the actual object you want to use with that pointer-to-member. This is what the this->* portion of the calling expression does. I.e. the call will always look as

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

or as

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

The left-hand side of the call (<pointer-to-object> or <object> above) cannot be omitted.

In other words, it doesn't matter whether fptr is a member variable, local variable, global variable or any other kind of variable, the call through fptr will always look as

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

assuming that you want to invoke it with *this object. If, for another example, you want to invoke it for some other object pointed by pointer pfoo, the call will look as follows

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

In your BAR class the call should look as (this->*fptr)(a,b) even though fptr is a local variable.

AndreyT
I was under the impression that *all* objects of the class would utilize a function with the *same* address --- so why do we need `pointer-to-object` when function address has nothing to with the object --- or am I mistaken about that?
Jacob
@Jacob: You are correct that all the functions use the same address, but if that member function modifies it's object, it has to know which instance of the class in question needs to be modified. That's achieved through the `this` pointer and is why you actually need an instance of the class before you can use the function pointer.
Billy ONeal
@AndreyT: +1 for correcting my answer :)
Billy ONeal
+1 for clarifying my misunderstanding, thanks for the explanation!
Jacob
@Jacob: Firstly, you cannot just call a non-static member function by itself, even if you know its exact address. Knowing the address is not enough. Every non-static member function has an implicit parameter `this`. This parameter needs an argument. This is exactly what you supply before `->*`: the argument for the implicit `this` parameter. Secondly, if the function is virtual, then the pointer is not attached to the specific version of the virtual function. The call will still be resolved through the VMT and for that we need to know the object.
AndreyT
A: 

When you invoke a member function of a class the compiler generates code to set 'this' while the function runs. When you call it from a function pointer that isn't done. There are ways to get around it but they aren't 'guaranteed' to work and are compiler dependent. You can do it as long as you're careful and know the possible problems you can run into.

Jay