函数指针


程序在执行时,函数在内存中也占有一个空间,将函数名称作为指定来源时,函数名称会自动转为指针,类型由返回值类型与参数列决定,若要将之指定给另一函数指针,类型的定义方式如下:

返回值类型 (*名称)(参数列);

函数指针代表着一个函数,相同类型的函数可以指定给具有相同类型的指针,例如:

#include <iostream> 
using namespace std; 

int foo(int); 

int main() { 
    int (*fp)(int) = foo; 

    foo(10);  // 显示 10
    fp(20);   // 显示 20

    return 0; 
} 

int foo(int n) { 
    cout << "n = " << n << endl; 
    return 0; 
}

foo指定给fp,等效于&foo指定给fp,在指定之后,fp存储了foo的地址,在调用时,fp(20)等效于(*fp)(20)

重载函数具有不同的签署,因此在指定时,虽然具有相同名称,然而依函数指针的类型不同,编译器会选择对应的重载函数:

#include <iostream> 
using namespace std; 

void foo(int); 
int foo(int, int);

int main() { 
    void (*fp)(int) = foo; 
    int (*add)(int, int) = foo; 

    foo(10);                                  // 显示 10
    cout << "1 + 2 = " << add(1, 2) << endl;  // 显示 1 + 2 = 3

    return 0; 
} 

void foo(int n) { 
    cout << "n = " << n << endl; 
}

int foo(int a, int b) { 
    return a + b;
}

函数指针可以用来传递函数,例如,你想编写用于数组的sort函数,希望大小顺序可以由调用者指定,这就可以传递函数来指定,例如:

#include <iostream> 
using namespace std; 

void sort(int*, int, bool (*compare)(int, int));
bool ascending(int, int);
bool descending(int, int);

int main() { 
    int number[] = {3, 5, 1, 6, 9};

    sort(number, 5, ascending);
    // 显示 1 3 5 6 9
    for(auto n : number) {
        cout << n << " ";
    }
    cout << endl;

    sort(number, 5, descending);
    // 显示 9 6 5 3 1
    for(auto n : number) {
        cout << n << " ";
    }
    cout << endl;

    return 0; 
} 

void swap(int &a, int &b) {
    int t = a; 
    a = b; 
    b = t;
}

void sort(int* arr, int length, bool (*compare)(int, int)) { 
    for(int flag = 1, i = 0; i < length - 1 && flag == 1; i++) { 
        flag = 0; 
        for(int j = 0; j < length - i - 1; j++) { 
            if(compare(arr[j + 1], arr[j])) { 
                swap(arr[j + 1], arr[j]); 
                flag = 1; 
            } 
        } 
    } 
}

bool ascending(int a, int b) {
    return a < b;
}

bool descending(int a, int b) {
    return a > b;
}

在这个例子中,sort上的函数指针定义有些难以阅读,可以使用typedef,定义一个比较容易阅读的名称,例如:

#include <iostream> 
using namespace std; 

typedef bool (*CMP)(int, int);

void sort(int*, int, CMP);
...略

void sort(int* arr, int length, CMP compare) { 
    for(int flag = 1, i = 0; i < length - 1 && flag == 1; i++) { 
        flag = 0; 
        for(int j = 0; j < length - i - 1; j++) { 
            if(compare(arr[j + 1], arr[j])) { 
                swap(arr[j + 1], arr[j]); 
                flag = 1; 
            } 
        } 
    } 
}

...略

C++ 11鼓励使用using来取代typedef,因为比较直觉,而且可以结合模版。例如:

using CMP = bool (*)(int, int);

另一个方式是使用decltype,这可以就一个既有的类型来进行类型定义。例如:

#include <iostream> 
using namespace std; 

bool cmp(int, int);

void sort(int*, int, decltype(cmp));
bool ascending(int, int);
bool descending(int, int);

...略

void sort(int* arr, int length, decltype(cmp) compare) { 
    for(int flag = 1, i = 0; i < length - 1 && flag == 1; i++) { 
        flag = 0; 
        for(int j = 0; j < length - i - 1; j++) { 
            if(compare(arr[j + 1], arr[j])) { 
                swap(arr[j + 1], arr[j]); 
                flag = 1; 
            } 
        } 
    } 
}

...略

在参数的类型定义复杂时,虽然不能使用autodecltype的运用可以稍微缓解一下类型定义的负担。

也可以定义函数指针数组,例如:

bool (*compare[10])(int, int) = {nullptr};

上面这个定义产生具有 10 个元素的数组,可以存储 10 个bool (*)(int, int)类型的函数地址,目前都初始化为nullptr,不过这样的定义实在难以阅读,可以使用using来改进:

using CMP = bool (*)(int, int);
CMP compare[10] = nullptr;

若是使用decltype的话,必须是:

bool cmp(int, int);
decltype(cmp) *compare[10] = {nullptr};

传递函数时多半使用函数指针,不过也可以创建函数参考,例如:

#include <iostream> 
using namespace std; 

int foo(int); 

int main() { 
    int (&fr)(int) = foo; 

    foo(10);  // 显示 10
    fr(20);   // 显示 20

    return 0; 
} 

int foo(int n) { 
    cout << "n = " << n << endl; 
    return 0; 
}

fr就只是foo的别名,也可以参数列上定义函数参考:

#include <iostream> 
using namespace std; 

void sort(int*, int, bool (&compare)(int, int));
bool ascending(int, int);
bool descending(int, int);

int main() { 
    int number[] = {3, 5, 1, 6, 9};

    sort(number, 5, ascending);
    // 显示 1 3 5 6 9
    for(auto n : number) {
        cout << n << " ";
    }
    cout << endl;

    sort(number, 5, descending);
    // 显示 9 6 5 3 1
    for(auto n : number) {
        cout << n << " ";
    }
    cout << endl;

    return 0; 
} 

...略

void sort(int* arr, int length, bool (&compare)(int, int)) { 
    for(int flag = 1, i = 0; i < length - 1 && flag == 1; i++) { 
        flag = 0; 
        for(int j = 0; j < length - i - 1; j++) { 
            if(compare(arr[j + 1], arr[j])) { 
                swap(arr[j + 1], arr[j]); 
                flag = 1; 
            } 
        } 
    } 
}

...略

可以创建函数指针的数组,然而,参考是别名,不是对象,无法创建参考的数组(array of references),或许这是传递函数时,多半使用函数指针的原因。

C++ 11 提供了function,定义于functional标头文件,该类的实例可以接受Callable对象,函数指针是其中之一,使用它来定义接受函数指针的参数,语法上会比较轻松一些,例如:

#include <iostream> 
#include <functional>
using namespace std; 

void sort(int*, int, function<bool(int, int)>);
bool ascending(int, int);
bool descending(int, int);

int main() { 
    int number[] = {3, 5, 1, 6, 9};

    sort(number, 5, ascending);
    // 显示 1 3 5 6 9
    for(auto n : number) {
        cout << n << " ";
    }
    cout << endl;

    sort(number, 5, descending);
    // 显示 9 6 5 3 1
    for(auto n : number) {
        cout << n << " ";
    }
    cout << endl;

    return 0; 
} 

...略

void sort(int* arr, int length, function<bool(int, int)> compare) { 
    for(int flag = 1, i = 0; i < length - 1 && flag == 1; i++) { 
        flag = 0; 
        for(int j = 0; j < length - i - 1; j++) { 
            if(compare(arr[j + 1], arr[j])) { 
                swap(arr[j + 1], arr[j]); 
                flag = 1; 
            } 
        } 
    } 
}

...略




展开阅读全文