成员指针


在〈函数指针〉介绍过如何将函数指定给对应类型的函数指针,类的成员函数也是函数,必要时也可以指向对应类型的指针。

要定义成员函数的指针,与非成员函数的指针定义类似,主要是要以::来指定是哪个类的成员函数,函数签署必须符合,以〈定义类〉的Account为例,可以如下定义:

void (Account::*mf1)(double) = nullptr;

mf1 = &Account::deposit;   
mf1 = &Account::withdraw;  

string (Account::*mf2)() = &Account::to_string;

上例中mf1可以接受的是Accountdepositwithdraw,而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来绑定第一个参数的值。

这就解答了另一个问题,怎么使用functionalfunction模版来定义成员函数类型呢?记得,第一个参数就是接受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是个数据成员指针,可以指向类定义的数据成员,实际上要代表哪个实例的值域还需指定,同样也可以透过.*(参考的时候)、->*(指针的时候) 来使用。


展开阅读全文