Function Pointers


Like variables, C++ functions are stored in the computer's memory (in the text segment). That means that a function has an address. When a function is called, your C++ program branches to the address of that function and begins executing the statements in the function body.

Since a C++ function has an address, we can declare a pointer variable to store that address. The syntax to declare a pointer to a function is a bit different than the syntax to declare a pointer to a variable, but the concept is similar. A pointer to a variable can be used to to access the value stored in the variable to which it points. A pointer to a function can be used to call the function to which it points.

A declaration for a pointer to a function or static member function looks like this in C++:

    return-type (*pointer-name)(parameter-data-types)

The syntax to declare a pointer to a non-static member function is slightly different:

    return-type (ClassName::*pointer-name)(parameter-data-types)

For example, a declaration for a pointer to a function that takes two integers as arguments and returns a Boolean value would look like this:

bool (*compare)(int, int)

while a declaration for a pointer to a member function of the class Fred that takes a float and a char as arguments and returns an integer would look like this:

int (Fred::*ptr)(float, char)

Note that the names for the pointers are entirely up to the programmer.)

You can obtain the address of a function using the unary & ("address-of") operator. The address can be assigned to a function pointer or (more commonly) can be passed to another function and copied into a function pointer parameter:

compare = &my_function;

Once the pointer compare contains the address of a function, we can use it to call the function - just use the pointer name exactly like a function name:

if (compare(num1, num2))
{
    // Code to execute if the function pointed to by compare returns true
}

Let's say that we want to sort an array of Student objects in ascending order by the data member name using the selection sort algorithm. If the sort code is defined in a method of a Course class that contains an array of Student objects named class_list and an integer num_students that specifies the number of objects in the array that have been filled with valid data, the method might look like this:

void Course::sort_class_list(bool (*compare)(const Student&, const Student&))
{
    int i, j, min;
    Student temp;

    for (i = 0; i < num_students - 1; i++)
    {
        min = i;
    
        for (j = i + 1; j < num_students; j++)
        {
            if (compare(class_list[j], class_list[min]))
                min = j;
        }
    
        temp = class_list[i];
        class_list[i] = class_list[min];
        class_list[min] = temp;
    }
}

In Course.cpp, we can define a comparison function that will let us check whether one Student object's name member is less than that of the other:

bool name_less(const Student& s1, const Student& s2)
{
    if (strcmp(s1.get_name(), s2.get_name()) < 0)
        return true;
    else
        return false;
}

Notes:

  1. Object types are normally passed by reference in C++ to avoid having to copy the object. Since this function will not alter either of the Student objects passed to it, we can declare each of parameters as a reference to a constant Student object.
  2. The names are C strings, so they must be compared using strcmp(). They are also private data, so we need to use the public get_name() accessor method to access them.
  3. You can make this code shorter by simply returning the conditional expression, since that will itself evaluate to true or false.

We put the prototype for this comparison function in Course.h, outside the declaration of the Course class:

bool name_less(const Student&, const Student&);

Once all of this is in place, we can call the sort_class_list() method on a Course object, supplying the address of the comparison function as the argument:

course1.sort_class_list(&name_less);

To sort the course's students in a different fashion, no modifications are required to the actual sort() method. Instead, we can just write another comparison function. For example, if we wanted to be able to sort in descending order by the students' GPA, we could add this function to Course.cpp (and its prototype to Course.h):

bool gpa_greater(const Student& s1, const Student& s2)
{
    return (s1.get_gpa() > s2.get_gpa());
}

Now the behavior of our sort() method can vary depending on what comparison function we pass to it:

cout << "Students listed in ascending order by name:\n\n";
course1.sort_class_list(&name_less);
course1.print();

cout << "\nStudents listed in descending order by GPA:\n\n";
course2.sort_class_list(&gpa_greater);
course2.print();

And because the comparison functions are not actually part of the Course class, it's easy for another programmer to add new comparison functions as desired.

That's just one example of how a pointer to a function can allow you to write code that is more generic and reusable.

Download a full program example, including a makefile and sample data files