This article is mainly excerpted from LearnCpp.com

Pointer to functions

The syntax for creating a non-const function pointer is one of the ugliest things you will ever see in C++:

// fcnPtr is a pointer to a function that takes no arguments and returns an integer
int (*fcnPtr)();

In the above snippet, fcnPtr is a pointer to a function that has no parameters and returns an integer. fcnPtr can point to any function that matches this type.

To make a const function pointer, the const goes after the asterisk:

int (*const fcnPtr)();

If you put the const before the int, then that would indicate the function being pointed to would return a const int.

Note that the type (parameters and return type) of the function pointer must match the type of the function. Here are some examples of this:

// function prototypes
int foo();
double goo();
int hoo(int x);
 
// function pointer assignments
int (*fcnPtr1)(){ &foo }; // okay
int (*fcnPtr2)(){ &goo }; // wrong -- return types don't match!
double (*fcnPtr4)(){ &goo }; // okay
fcnPtr1 = &hoo; // wrong -- fcnPtr1 has no parameters, but hoo() does
int (*fcnPtr3)(int){ &hoo }; // okay

Unlike fundamental types, C++ will implicitly convert a function into a function pointer if needed (so you don’t need to use the address-of operator (&) to get the function’s address). However, it will not implicitly convert function pointers to void pointers, or vice-versa.

Calling a function using a function pointer

The other primary thing you can do with a function pointer is use it to actually call the function. There are two ways to do this:

int foo(int x) { return x; }
int main()
{
    int (*fcnPtr)(int){ &foo }; // Initialize fcnPtr with function foo
    (*fcnPtr)(5); // call function foo(5) via explict dereference of fcnPtr.
    fcnPtr(5); // call function foo(5) via implicit dereference of fcnPtr.
    return 0;
}

As you can see, the implicit dereference method looks just like a normal function call — which is what you’d expect, since normal function names are pointers to functions anyway! However, some older compilers do not support the implicit dereference method, but all modern compilers should.

One interesting note: Default parameters won’t work for functions called through function pointers. Default parameters are resolved at compile-time (that is, if you don’t supply an argument for a defaulted parameter, the compiler substitutes one in for you when the code is compiled). However, function pointers are resolved at run-time. Consequently, default parameters can not be resolved when making a function call with a function pointer. You’ll explicitly have to pass in values for any defaulted parameters in this case.

The implementation of function pointers is simple: they are just “code pointers”: they hold the starting address of an assembly-language routine. The different types of function pointers exist only to ensure that the correct calling convention is used.

The pointer to member functions

adapted from ref2

A member function pointer (MFP) to a member function of class SomeClass, with the same arguments as before, is declared like this:

float (SomeClass::*my_memfunc_ptr)(int, char *);
// For const member functions, it's declared like this:
float (SomeClass::*my_const_memfunc_ptr)(int, char *) const;

Notice that a special operator (::*) is used, and that SomeClass is part of the declaration. Member function pointers have a horrible restriction: they can only point to member functions of a single class. There is a different type of member function pointer for each combination of arguments, for both types of const-ness, and for each class.

If you’re using member function pointers, you should always use a typedef to avoid confusion.

You make your function pointer point to a function float SomeClass::some_member_func(int, char *) like this:

my_memfunc_ptr = &SomeClass::some_member_func;
// This is the syntax for operators: 
my_memfunc_ptr = &SomeClass::operator !;
// There is no way to take the address of a constructor or destructor

To invoke the member function pointer, you need to provide an instance of SomeClass, and you must use the special operator ->*. This operator has a low precedence, so you need to put it in parentheses:

SomeClass *x = new SomeClass;
(x->*my_memfunc_ptr)(6, "Another Arbitrary Parameter");
// You can also use the .* operator if your class is on the stack.
SomeClass y;
(y.*my_memfunc_ptr)(15, "Different parameters this time");

In writing this article, I have one key point to make: It is absurd that the C++ Standard allows you to cast between member function pointers, but doesn’t allow you to invoke them once you’ve done it. It’s absurd for three reasons. Firstly, the cast won’t always work on many popular compilers (so, casting is standard, but not portable). Secondly, on all compilers, if the cast is successful, invoking the cast member function pointer behaves exactly as you would hope: there is no need for it to be classed as “undefined behavior”. (Invocation is portable, but not standard!) Thirdly, allowing the cast without allowing invocation is completely useless; but if both cast and invocation are possible, it’s easy to implement efficient delegates, with a huge benefit to the language.

You’d probably guess that a “member function pointer”, like a normal function pointer, just holds a code pointer. You would be wrong. On almost all compilers, a member function pointer is bigger than a function pointer. Most bizarrely, in Visual C++, a member function pointer might be 4, 8, 12, or 16 bytes long, depending on the nature of the class it’s associated with, and depending on what compiler settings are used!

【此前的成员函数指针】Let’s go back in time to the early 1980’s. When the original C++ compiler (CFront) was originally developed, it only had single inheritance. When member function pointers were introduced, they were simple: they were just function pointers that had an extra this parameter as the first argument. When virtual functions were involved, the function pointer pointed to a short bit of ‘thunk’ code. (Update, Oct 04: A discussion on comp.lang.c++.moderated has established that CFront didn’t actually use thunks, it was much less elegant. But it could have used this method, and pretending that it did, makes the following discussion a bit easier to understand.)

【引入了多继承之后的成员函数指针】This idyllic world was shattered when CFront 2.0 was released. It introduced templates and multiple inheritance. Part of the collateral damage of multiple inheritance was the castration of member function pointers. The problem is, with multiple inheritance, you don’t know what this pointer to use until you make the call. For example, suppose you have the four classes defined below:

class A {
 public:
       virtual int Afunc() { return 2; };
};
 
class B {
 public: 
      int Bfunc() { return 3; };
};
 
// C is a single inheritance class, derives only from A
class C: public A {
 public: 
     int Cfunc() { return 4; };
};
 
// D uses multiple inheritance
class D: public A, public B {
 public: 
    int Dfunc() { return 5; };
};

Suppose we create a member function pointer for class C. In this example, Afunc and Cfunc are both member functions of C, so our member function pointer is allowed to point to Afunc or Cfunc. But Afunc needs a this pointer that points to C::A (which I’ll call Athis), while Cfunc needs a this pointer that points to C (which I’ll call Cthis). Compiler writers deal with this situation by a trick: they ensure that A is physically stored at the start of C. This means that Athis == Cthis. We only have one this to worry about, and all’s well with the world.

Now, suppose we create a member function pointer for class D. In this case, our member function pointer is allowed to point to AfuncBfunc, or Dfunc. But Afunc needs a this pointer that points to D::A, while Bfunc needs a this pointer that points to D::B. This time, the trick doesn’t work. We can’t put both A and B at the start of D. So, a member function pointer to D needs to specify not only what function to call, but also what this pointer to use. The compiler does know how big A is, so it can convert an Athis pointer into a Bthis just by adding an offset (delta = sizeof(A)) to it.

If you’re using virtual inheritance (i.e., virtual base classes), it’s much worse, and you can easily lose your mind trying to understand it. Typically, the compiler uses a virtual function table (‘vtable’) which stores for each virtual function, the function address, and the virtual_delta: the amount in bytes that needs to be added to the supplied this pointer to convert it into the this pointer that the function requires.

In theory, all of these vendors could radically change their technique for representing MFPs. In practice, this is extremely unlikely, because it would break a lot of existing code. At MSDN, there is a very old article that was published by Microsoft which explains the run-time implementation details of Visual C++ [JanGray]. It’s written by Jan Gray, who actually wrote the MS C++ object model in 1990. Although the article dates from 1994, it is still relevant - excluding the bug fix, Microsoft hasn’t changed it for 15 years. Similarly, the earliest compiler I have (Borland C++ 3.0, (1990)) generates identical code to Borland’s most recent compiler, except of course that 16 bit registers are replaced with 32 bit ones.

By now, you know far too much about member function pointers. What’s the point? I’ve dragged you through this to establish a rule. Although these implementations are very different from one another, they have something useful in common: the assembly code required to invoke a member function pointer is identical, regardless of what class and parameters are involved. Some compilers apply optimizations depending on the inheritance nature of the class, but when the class being invoked is of incomplete type, all such optimizations are impossible. This fact can be exploited to create efficient delegates.


Did you know the pointer to member function typically has the twice (not twice, but related to the class) size of the normal function pointers? I.e., suppose the pointer to functions is 8 bytes, then the pointer to member functions is 16 bytes.

Why?

See this stack overflow question: https://stackoverflow.com/a/12006882 , For a lot of detail, see this (scroll to “Implementations of Member Function Pointers”).

成员函数指针 function-pointer

SO interpretation

In the most normal situation, you can pretty much think of

struct A {
    int i;
    int foo() { return i; }
};
 
A a;
a.foo();

as

struct A {
    int i;
};
int A_foo( A* this ) { return this->i; };
 
A a;
A_foo(&a);

(Starting to look like C, right?) So you would think the pointer &A::foo would just be the same as a normal function pointer. But there are a couple of complications: Multiple inheritance, and virtual functions.

References

  1. 12.1 — Function Pointers
  2. Member Function Pointers and the Fastest Possible C++ Delegates