發新話題

C++ Gossip - 函式《進階議題》函式指標

C++ Gossip - 函式《進階議題》函式指標

程式在執行時,函式本身在記憶體中也佔有一個空間,而函式名稱本身也就是指向該空間位址的參考名稱,當呼叫函式名稱時,程式就會去執行該函式名稱所指向的記憶體空間中之指令。

您可以宣告函式指標,並讓它與某個函式指向相同的空間,函式指標的宣告方式如下所示:

傳回值型態 (*指標名稱)(傳遞參數);


一個函式型態由傳回值型態與參數列決定,不包括函式名稱,一個函式指標可指向具有相同型態的函式,也就是具有相同傳回值型態和參數列的函式。

下面這個程式是個簡單的示範,它以函式指標ptr來取得函式foo()的位址,使用它來呼叫函式,將與使用foo()來呼叫函式具有相同的作用,程式以整數方式顯示兩個的記憶體空間是相同的:


#include <iostream>
using namespace std;

int foo();

int main() {
    int (*ptr)() = 0;

    ptr = foo;

    foo();
    ptr();

    cout << "address of foo:"
         << (int)foo << endl;
    cout << "address of foo:"
         << (int)ptr << endl;

    return 0;
}

int foo() {
    cout << "function point" << endl;

    return 0;
}
執行結果:
function point
function point
address of foo:4199502
address of foo:4199502

如果函式帶有參數,則函式指標本身的宣告也必須指定相同的參數型態與個數,下面這個程式用來顯示重載函式會分別佔據不同的記憶體空間:

#include <iostream>
using namespace std;

int foo(int, int);
char foo(int, char);

int main() {
    int (*ptr1)(int, int) = 0;
    char (*ptr2)(int, char) = 0;

    ptr1 = foo; // get address of foo(int, int)
    ptr2 = foo; // get address of foo(int, char)

    ptr1(1, 2);
    ptr2(3, 'c');

    cout << "address of foo(int, int): "
         << (int)ptr1 << endl;
    cout << "address of foo(int, char): "
         << (int)ptr2 << endl;

    return 0;
}

int foo(int n1, int n2) {
    cout << "foo(int, int): "
         << n1 << "\t" << n2
         << endl;

    return 0;
}

char foo(int n, char c) {
    cout << "foo(int, char): "
         << n << "\t" << c
         << endl;

    return c;
}
執行結果:
foo(int, int): 1        2
foo(int, char): 3       c
address of foo(int, int): 4199548
address of foo(int, char): 4199648

由於在宣告函式指標時已指定了參數列的資料型態與個數,所以雖然在指定ptr1與ptr2時都使用foo函式名稱,但程式仍可以自動判別出我們是要指定哪個過載函式的位址。

來看看一個應用的例子,假設您要撰寫一個排序sort()函式,您希望排序時可以由大至小,也可以由小至大,比較簡單的作法是在sort()上加上一個額外的參數,可以傳入常數或列舉,例如如果指定1的話就由大至小,指定0的話就由小至大,這需要在函式中加上額外的判斷,為了簡化sort()的撰寫,可以傳入一個函式位址,函式中就無需額外的判斷,例如:
  • sort.h
void swap(int&, int&);
bool larger(int a, int b);
bool smaller(int a, int b);
void sort(int*, int, bool (*compare)(int, int));
  • sort.cpp
#include "sort.h";

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

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

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

void sort(int* arr, int length, bool (*compare)(int, int)) {
    int flag = 1;

    for(int 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;
            }
        }
    }
}
  • main.cpp
#include <iostream>
#include "sort.h"
using namespace std;

int main() {
    int number1[] = {3, 5, 1, 6, 9};
    sort(number1, 5, larger);
    cout << "大的在前 ";
    for(int i = 0; i < 5; i++) {
        cout << number1 << " ";
    }
    cout << endl;

    int number2[] = {3, 5, 1, 6, 9};
    sort(number2, 5, smaller);
    cout << "小的在前 ";
    for(int i = 0; i < 5; i++) {
        cout << number2 << " ";
    }
    cout << endl;

    return 0;
}
執行結果:
大的在前 9 6 5 3 1
小的在前 1 3 5 6 9

在函式中,不必理會傳入的實際函式,只要呼叫compare()就可以了,在這個例子中,您的sort()上的函式指標宣告有些難以閱讀,您可以使用 typedef,定義一個比較容易閱讀的名稱,例如:
  • sort.h
typedef bool (*CMP)(int, int);
void swap(int&, int&);
bool larger(int a, int b);
bool smaller(int a, int b);
void sort(int*, int, CMP);
  • sort.cpp
#include "sort.h"

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

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

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

void sort(int* arr, int length, CMP compare) {
    int flag = 1;

    for(int 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;
            }
        }
    }
}
可以看到,重新使用typedef定義CMP名稱後,函式比較容易閱讀的多了。

您也可以宣告函式指標陣列,例如:

bool (*compare[10])(int, int);


上面這個宣告產生具有10個元素的陣列,可以儲存10個sort函式型態的位址,不過這樣的宣告實在難以閱讀,可以使用typedef來改進:
typedef bool (*CMP)(int, int);
CMP compare[10];


可以看到這次的宣告比較容易閱讀了。

TOP

發新話題

本站所有圖文均屬網友發表,僅代表作者的觀點與本站無關,如有侵權請通知版主會盡快刪除。