Pointing a Variable to a Member Function in C++ - dummies

Pointing a Variable to a Member Function in C++

By John Paul Mueller, Jeff Cogswell

It’s surprising to find out that most C++ programmers have no idea that a pointer called this exists. So this is a big secret! Revel in it! What is the secret? The secret is that you can take the address of an object’s member function so that you can access the member function instance data directly. Ooh-wee!

Now, remember that each instance of a class gets its own copy of the member variables, unless the variables are static. But functions are shared throughout the class. Yes, you can distinguish static functions from nonstatic functions.

But that just refers to what types of variables they access: Static functions can access only static member variables, and you don’t need to refer to them with an instance. Nonstatic (that is, normal, regular) member functions work with a particular instance. However, inside the memory, really only one copy of the function exists.

So how does the member function know which instance to work with? A secret parameter gets passed into the member function: the this pointer. Suppose you have a class called Gobstopper that has a member function called Chew(). Next, you have an instance called MyGum, and you call the Chew() function like so:

MyGum.Chew();

When the compiler generates assembly code for this, it actually passes a parameter into the function — the address of the MyGum instance, also known as the thispointer. Therefore only one Chew() function is in the code, but to call it you must use a particular instance of the class.

Because only one copy of the Chew() function is in memory, you can take its address. But to do so requires some sort of cryptic-looking code. Here it is, quick and to the point. Suppose your class looks like this:

class Gobstopper {
public:
    int WhichGobstopper;
    int Chew(string name) {
        cout << WhichGobstopper << endl;
        cout << name << endl;
        return WhichGobstopper;
    }
};

The Chew() function takes a string and returns an integer. Here’s a typedef for a pointer to the Chew() function:

typedef int (Gobstopper::*GobMember)(string);

And here’s a variable of the type GobMember:

GobMember func = &Gobstopper::Chew;

If you look closely at the typedef, it looks similar to a regular function pointer. The only difference is that the classname and two colons precede the asterisk. Other than that, it’s a regular old function pointer.

But whereas a regular function pointer is limited to pointing to functions of a particular set of parameter types and a return type, this function pointer shares those restrictions but has a further limitation: It can only point to member functions within the class Gobstopper.

To call the function stored in the pointer, you need to have a particular instance. Notice that in the assignment of func in the earlier code there was no instance, just the classname and function, &Gobstopper::Chew. So to call the function, grab an instance, add func, and go!

The FunctionPointer02 example shown contains a complete example with the class, the member function address, and two separate instances.

#include <iostream>
#include <string>
using namespace std;
class Gobstopper
{
public:
    int WhichGobstopper;
    int Chew(string name) {
        cout << WhichGobstopper << endl;
        cout << name << endl;
        return WhichGobstopper;
    }
};
int main()
{
    typedef int (Gobstopper::*GobMember)(string);
    GobMember func = &Gobstopper::Chew;
    Gobstopper inst;
    inst.WhichGobstopper = 10;
    Gobstopper another;
    another.WhichGobstopper = 20;
    (inst.*func)("Greg W.");
    (another.*func)("Jennifer W.");
    return 0;
}

You can see in main that first you create the type for the function, which you call GobMember, and then you create a variable, func, of that type. Then you create two instances of the Gobstopper class, and you give them each a different WhichGobstopper value.

Finally, you call the member function, first for the first instance and then for the second instance. Just to show that you can take the addresses of functions with parameters, you pass in a string with some names.

When you run the code, you can see from the output that it is indeed calling the correct member function for each instance:

10
Greg W.
20
Jennifer W.

Now, when you say “the correct member function for each instance,” what you really mean is that the code is calling the same member function each time but using a different instance. If you’re thinking in object-oriented terms, consider each instance as having its own copy of the member function. Therefore, it’s okay to say “the correct member function for each instance.”