2014年1月19日 星期日

C++ 函式指標、指標與類別成員

以下介紹
1.指標指到函式(函式指標) (Pointers to Functions)
2.指標指到物件的成員函式 (Pointer to Member Function)
3.指標指到物件的資料成員 (Pointer to Data Member)
4.指標指到 Class 的 Static Member


※函式指標 (Function Pointer)
C 語言指標可以指到一個變數的記憶體位址。
而 function block 也占有記憶體空間,因此指標也可以指到函式的記憶體位址,
指到函式記憶體位址的指標,稱為函式指標(function pointer)。

函式指標的宣告與呼叫執行
函式指標的宣告,需注意
1.與函式的回傳型態相同
2.與函式的參數數量、參數型態相同

宣告格式:
函式回傳型態 (*指標名稱) (參數1型態, 參數2型態, ...)
調用格式:(使用函式指標呼叫執行函式)
指標名稱 (參數1, 參數2, ...) 
// 也可以寫成
(*指標名稱) (參數1, 參數2, ...)

註:
1.當函式名稱之後沒有緊跟著呼叫運算子"()",則函式名稱可視為指標。
2.取址運算子"&",作用在函式名稱上時,得到的也是相同的指標,所以 fn 和 &fn 是一樣的

範例:
#include <iostream>
using namespace std;
int fn1(char x, int y){
    //...
    return 0;
}
int fn2(double x){
    //...
    return 0;
}
int main()
{
    // 函式指標宣告  
    int (*fptr) (char, int); // fptr 宣告為 Function Pointer
    // 設定 fptr 指向 fn1 函式的記憶體位址
    fptr = fn1; // OK
    // fptr = fn2; // Error! 參數個數、型態不同

    fptr('a', 3); // 呼叫執行
    (*fptr)('a', 3); // 呼叫執行 也可以這樣寫

    return 0;
}

函式指標的用處
方便在程式執行階段,根據不同狀況,執行不同的函數。
範例:
#include <iostream>
using namespace std;

int add(int x, int y){
    return x + y;
}
int sub(int x, int y){
    return x - y;
}
int main()
{
    int (*fptr) (int, int);
    fptr = NULL;

    // 根據不同狀況,選擇使用 add 或 sub 函式
    char r = '+';
    switch(r){
    case '+':
        fptr = add;
        break;
    case '-':
        fptr = sub;
        break;
    }

    cout << fptr(3, 2) << endl; // 執行結果:5 

    return 0;
}

在陣列中存放函式指標
將函數指標的宣告當做陣列的型別,此陣列即可存放函式指標。
#include <iostream>
using namespace std;

int add(int x, int y){
    return x + y;
}
int sub(int x, int y){
    return x - y;
}
int main()
{
    typedef int (*fptr) (int, int);

    // 之後索引用來存放ASCII字元,ASCII有128個字元,128個元素都初始化為 NULL
    fptr fptrAry[128] = {NULL};
    // 上一行也可直接寫成 int (*fptrAry[128]) (int x, int y) = {NULL};

    fptrAry['+'] = add;
    fptrAry['-'] = sub;

    cout << fptrAry['+'](3, 2) << endl; // 5 
    cout << fptrAry['-'](3, 2) << endl; // 1

    return 0;
}

函數指標的比較
函數指標也可以用比較運算子,來判斷是否相等。
範例:
#include <iostream>
using namespace std;

int add(int x, int y){
    return x + y;
}
int main()
{
    int (*fptr) (int, int);
    fptr = add;
    if(fptr == add){
        // 相等
    }else{
        // 不相等
    }

    return 0;
}

函數指標之間的轉型
函數指標可以轉型成其他型別的函數指標
範例:
#include <iostream>
using namespace std;

int fn1(int x, int y){
    return x + y;
}
int fn2(int x){
    return x;
}
int main()
{
    typedef int (*fptr1) (int, int);
    fptr1 p1= fn1;

    typedef int (*fptr2) (int);
    // 將 p1 轉型為 fptr2
    fptr2 p1Tmp1 = (fptr2) p1;
    // 再轉回 fptr1
    fptr1 p1Tmp2 = (fptr1) p1Tmp1;

    // cout << p1Tmp1(1) << endl; // 錯誤,得到無法預期的結果

    // 轉回原來的型別後,會跟之前的結果一樣
    cout << p1Tmp2(1,2) << endl; // 3

    return 0;
}


※指標指到物件的成員函式 (Pointer to Member Function)
指標要指到物件的 member function 時,宣告和調用時,都跟指到一般的 function 不太一樣。

宣告格式:
函式回傳型態 (類別名稱::*指標名稱) (參數1型態, 參數2型態, ...)
宣告時,必須標明屬於哪一個類別

調用格式:
(物件實例.*指標名稱) (參數1, 參數2, ...)
調用時,要經由物件實例來調用。

範例:
#include <iostream>
using namespace std;
class Test{
public:
    int mm(int x, int y)
    {
        return x + y;
    }
    // member function 不是放在個別 object 的空間,
    // 而是所有同類別物件共享同一個 member function
};
int main()
{
    Test t;
    int (Test::*p)(int,int); // p 是成員函式指標
    p = &Test::mm; // 將 p 指到 Test 類別的 mm() 函式 
    /*
    也可用 typedef 改寫成 
    typedef int (Test::*MyPtr)(int, int);
    MyPtr p = &Test::mm;
    */

    cout << (t.*p)(2,3)<< endl; // 5

    return 0;
}


※指標指到物件的資料成員函式 (Pointer to Data Member)
宣告格式:
型態 類別名稱::*指標名稱
宣告時,必須標明屬於哪一個類別

調用格式:
物件實例.*指標名稱
調用時,要經由物件實例來調用。

範例:
#include <iostream>
using namespace std;
class Test{
public:
    int a;
};
int main()
{
    int Test::*p = &Test::a;
    /*
    也可用 typedef 改寫成 
    typedef int Test::*MyPtr;
    MyPtr p = &Test::a;
    */

    Test t1; // 要先有實體物件,才能調用
    t1.*p = 10;

    Test t2; // 要先有實體物件,才能調用
    t2.*p = 20;

    cout << t1.*p << endl; // 10
    cout << t2.*p << endl; // 20

    return 0;
}


※指標指到 Class 的 Static Member
指標指到類別裡面靜態成員時,用法跟一般變數指標、一般函數指標一樣。
範例:
#include <iostream>
using namespace std;
class Test{
public:
    static int a;
    static int mm()
    {
        return a;
    }
};

int Test::a = 10;
int main()
{

    // 跟使用一般變數指標一樣
    int * ptr;
    ptr = &Test::a;
    *ptr = 20;
    cout << Test::a << endl; // 20

    // 跟使用一般函式指標一樣
    int (*fptr)(); 
    fptr = &Test::mm;
    cout << Test::mm() << endl; // 20

    return 0;
}

沒有留言:

張貼留言