在〈函式指標〉介紹過如何將函式指定給對應型態的函式指標,類別的成員函式也是函式,必要時也可以指向對應型態的指標。
要宣告成員函式的指標,與非成員函式的指標宣告類似,主要是要以 ::
來指定是哪個類別的成員函式,函式簽署必須符合,以〈定義類別〉的 Account
為例,可以如下宣告:
void (Account::*mf1)(double) = nullptr;
mf1 = &Account::deposit;
mf1 = &Account::withdraw;
string (Account::*mf2)() = &Account::to_string;
上例中 mf1
可以接受的是 Account
的 deposit
與 withdraw
,而 mf2
可以接受的是 to_string
,類別的實例會共用成員函式,呼叫成員函式時,必須將提供實例的位址給成員函式中的 this
指標,例如:
#include <iostream>
#include <string>
#include "account.h"
void call(Account &self, void (Account::*member)(double), double param) {
(self.*member)(param);
}
int main() {
Account acct = {"123-456-789", "Justin Lin", 1000};
call(acct, &Account::deposit, 1000);
call(acct, &Account::withdraw, 500);
cout << acct.to_string() << endl;
return 0;
}
如果 self
是個指標,就要透過 ->
,例如:
#include <iostream>
#include "account.h"
void call(Account *self, void (Account::*member)(double), double param) {
(self->*member)(param);
}
int main() {
Account acct = {"123-456-789", "Justin Lin", 1000};
call(&acct, &Account::deposit, 1000);
call(&acct, &Account::withdraw, 500);
cout << acct.to_string() << endl;
return 0;
}
在 functional
標頭中定義有 mem_fn
函式,接受成員函式,傳回的呼叫物件,可以指定呼叫者收者,例如:
#include <iostream>
#include <functional>
#include "account.h"
void call(Account &self, void (Account::*member)(double), double param) {
mem_fn(member)(self, param);
}
int main() {
Account acct = {"123-456-789", "Justin Lin", 1000};
call(acct, &Account::deposit, 1000);
call(acct, &Account::withdraw, 500);
cout << acct.to_string() << endl;
return 0;
}
指定呼叫者時可以是個值,這相當於指定 *this
參考的對象,也可以是個指標,這就是指定 this
:
#include <iostream>
#include <functional>
#include "account.h"
void call(Account *self, void (Account::*member)(double), double param) {
mem_fn(member)(self, param);
}
int main() {
Account acct = {"123-456-789", "Justin Lin", 1000};
call(&acct, &Account::deposit, 1000);
call(&acct, &Account::withdraw, 500);
cout << acct.to_string() << endl;
return 0;
}
也許你會想起〈高階函式〉中的 bind
函式,它也可以用來綁定 this
,例如:
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
void call(Account &self, void (Account::*member)(double), double param) {
bind<void>(member, &self, _1)(param);
}
int main() {
Account acct = {"123-456-789", "Justin Lin", 1000};
call(acct, &Account::deposit, 1000);
call(acct, &Account::withdraw, 500);
cout << acct.to_string() << endl;
return 0;
}
為什麼要將 &self
當成是第一個參數呢?對於一個方法,例如 void Account::deposit(double amount)
,可以想像成編譯器將之轉換為 void Account::deposit(Account *this, double amount)
,而對於 acct.deposit(1000)
時,可以想像成編譯器將之轉換為 Account::deposit(&acct, 1000)
,實際上程式碼這麼寫不會編譯成功,因此才說是想像,然而可以透過 bind
來綁定第一個參數的值。
這就解答了另一個問題,怎麼使用 functional
的 function
模版來宣告成員函式型態呢?記得,第一個參數就是接受 this
,因此就會是…
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
void call(Account &self, function<void(Account*, double)> member, double param) {
bind<void>(member, &self, _1)(param);
}
int main() {
Account acct = {"123-456-789", "Justin Lin", 1000};
call(acct, &Account::deposit, 1000);
call(acct, &Account::withdraw, 500);
cout << acct.to_string() << endl;
return 0;
}
那麼 static
成員函式呢?在〈static 成員〉中談過,static
成員屬於類別,某些程度上,就是將類別當成是一種名稱空間,實際上與一般函式無異,因此,函式指標的宣告與一般函式無異:
double (*fn)(double) = Math::toRadian;
類似類別的成員函式指標,也可以宣告類別的資料成員指標,例如:
#include <iostream>
using namespace std;
class Point {
public:
int x;
int y;
Point(int x, int y) : x(x), y(y) {}
};
void printCord(Point &pt, int Point::*cord) {
cout << pt.*cord << endl;
}
int main() {
Point pt(10, 20);
printCord(pt, &Point::x);
printCord(pt, &Point::y);
return 0;
}
在上例中,cord
是個資料成員指標,可以指向類別定義的資料成員,實際上要代表哪個實例的值域還需指定,同樣也可以透過 .*
(參考的時候)、->*
(指標的時候) 來使用。