Let’s clarify this process with an example. Suppose you want to design an estimate() function that estimates the amount of time necessary to write a given number of lines of code, and you want different programmers to use the function. Part of the code for estimate() will be the same for all users, but the function will allow each programmer to provide his or her own algorithm for estimating time. The mechanism for that will be to pass to estimate() the address of the particular algorithm function the programmer wants to use. To implement this plan, you need to be able to do the following:

• Obtain the address of a function.

• Declare a pointer to a function.

• Use a pointer to a function to invoke the function.

Obtaining the Address of a Function

Obtaining the address of a function is simple: You just use the function name without trailing parentheses. That is, if think() is a function, then think is the address of the function. To pass a function as an argument, you pass the function name. Be sure you distinguish between passing the address of a function and passing the return value of a function:

process(think);    // passes address of think() to process()

thought(think());  // passes return value of think() to thought()

The process() call enables the process() function to invoke the think() function from within process(). The thought() call first invokes the think() function and then passes the return value of think() to the thought() function.

Declaring a Pointer to a Function

To declare pointers to a data type, the declaration has had to specify exactly to what type the pointer points. Similarly, a pointer to a function has to specify to what type of function the pointer points. This means the declaration should identify the function’s return type and the function’s signature (its argument list). That is, the declaration should provide the same information about a function that a function prototype does. For example, suppose Pam LeCoder has written a time-estimating function with the following prototype:

double pam(int);  // prototype

Here’s what a declaration of an appropriate pointer type looks like:

double (*pf)(int);   // pf points to a function that takes

                     // one int argument and that

                     // returns type double

Tip

In general, to declare a pointer to a particular kind of function, you can first write a prototype for a regular function of the desired kind and then replace the function name with an expression in the form (*pf). In this case, pf is a pointer to a function of that type.

The declaration requires the parentheses around *pf to provide the proper operator precedence. Parentheses have a higher precedence than the * operator, so *pf(int) means pf() is a function that returns a pointer, whereas (*pf)(int) means pf is a pointer to a function:

double (*pf)(int); // pf points to a function that returns double

double *pf(int);   // pf() a function that returns a pointer-to-double

After you declare pf properly, you can assign to it the address of a matching function:

double pam(int);

double (*pf)(int);

pf = pam;           // pf now points to the pam() function

Note that pam() has to match pf in both signature and return type. The compiler rejects nonmatching assignments:

double ned(double);

int ted(int);

double (*pf)(int);

pf = ned;         // invalid -- mismatched signature

pf = ted;         // invalid -- mismatched return types

Let’s return to the estimate() function mentioned earlier. Suppose you want to pass to it the number of lines of code to be written and the address of an estimating algorithm, such as the pam() function. It could have the following prototype:

void estimate(int lines, double (*pf)(int));

This declaration says the second argument is a pointer to a function that has an int argument and a double return value. To have estimate() use the pam() function, you pass pam()’s address to it:

estimate(50, pam); // function call telling estimate() to use pam()

Clearly, the tricky part about using pointers to functions is writing the prototypes, whereas passing the address is very simple.

Using a Pointer to Invoke a Function

Now we get to the final part of the technique, which is using a pointer to call the pointed-to function. The clue comes in the pointer declaration. There, recall, (*pf) plays the same role as a function name. Thus, all you have to do is use (*pf) as if it were a function name:

double pam(int);

double (*pf)(int);

pf = pam;            // pf now points to the pam() function

double x = pam(4);   // call pam() using the function name

double y = (*pf)(5); // call pam() using the pointer pf

Actually, C++ also allows you to use pf as if it were a function name:

double y = pf(5);    // also call pam() using the pointer pf

Using the first form is uglier, but it provides a strong visual reminder that the code is using a function pointer.

History Versus Logic

Перейти на страницу:

Все книги серии Developer's Library

Похожие книги