成员指针(pointer to member)是指可以指向类的非静态成员的指针。
4.1 数据成员指针
const string Screen::*pdata = &Screen::contents;
class Screen {
public:
static const std::string Screen::*data()
{ return &Screen::contents; }
};
4.2 成员函数指针
char (Screen::*pmf2)(Screen::pos, Screen::pos) const;
pmf2 = &Screen::get;
和普通函数指针不同,成员函数和指向该成员的指针之间不存在自动转换规则。因为函数调用运算符的优先级较高,所以在声明指向成员函数的指针并使用这一的指针进行函数调用时,括号必不可少。
Screen myScreen, *pScreen = &myScreen;
char c1 = (pScreen->*pmf)();
char c2 = (muScreen.*pmf2)(0, 0);
使用成员指针的类型别名
using Action = char (Screen::*)(Screen::pos, Screen::pos) const;
Action get = &Screen::get;
Screen& action(Screen &, Action = &Screen::get);
成员指针函数表
对于普通函数指针和指向成员函数的指针来说,一种常见的用法是将其存入一个函数表中。如果一个类含有几种相同类型的成员,这样一张表可以帮助我们从这些成员中选择一个:
class Screen {
public:
//
Screen& home();
Screen& forward();
Screen& back();
Screen& up();
Screen& down();
using Action = Screen& (Screen::*)();
enum Direction { HOME, FORWARD, BACK, UP, DOWN};
Screen& move(Direction);
private:
static Action Menu[]; //函数表
};
Screen& Screen::move(Direction cm)
{
return (this->*Menu[cm])();
}
static::Action Screen::Menu[] = { &Screen::home,
&Screen::forward,
&Screen::back,
&Screen::up,
&Screen::down
};
4.3 将成员函数用作可调用对象
要想通过一个很指向成员函数的指针进行函数调用,必须先利用.*运算符或->*运算符将该指针绑定到特定对象上。因此和普通的函数指针不同,成员指针不是一个可调用对象,因为不支持函数调用运算符。
auto fp = &string::empty;
//错误,必须使用*.或->*调用成员指针
find_if( svec.begin(), svec.end(), fp);
使用function生成一个可调用对象
从指向成员函数的指针获取可调用对象的一种方法是使用标准库模板function。
#include <function>
function<bool (const string&)> fcn = &string::empty;
find_if( svec.begin(), svec.end(), fcn);
通常情况下,执行成员函数的对象将被传给隐式的this形参。当我们想要使用function为成员函数生成一个可调用对象,必须首先“翻译”该代码,使得隐式的形参变成显式的。
if(fcn(*it))
=>
if(((*it).*p)())
当我们定义一个function对象时,必须指定该对象所能表示的函数类型,即可调用对象的形式。如果可调用对象是一个成员函数,则第一个形参必须表示该成员是在哪个(一般是隐式的)对象上执行的。同时,提供给function的形式还必须指明对象是否是以指针或引用的形式传入的。
#include <function>
vector<string *> pvec;
function<bool (const string*)> fp = &string::empty;
find_if( pvec.begin(), pvec.end(), fp);
使用men_fn生成一个可调用对象
除了使用function之外,还可以通过使用标准库功能mem_fn来让编译器负责推断成员的类型。mem_fn也定义在头文件function中,并且可以从成员指针生成一个可调用对象:和function不同的是men_fn可以根据成员指针的类型推断可调用对象的类型,而无须用户显式地指定:
find_if( svec.begin(), svec.end(), mem_fn(&string::empty));
auto f = mem_fn(&string::empty); //f接受一个string或者一个string*
f(*svec.begin()); //正确:传入string对象,f使用.*调用empty
f(&svec[0]); //正确:传入一个string的指针,f->*调用empty
使用bind生成一个可调用对象
出于完整性的考虑,我们还可以使用bind从成员函数生成一个可调用对象:
auto it = find_if( svec.begin(), svec.end(),
bind(&string::empty, _1));
和function类似,我们使用bind时,必须将函数中用于表示执行对象的隐式形参转换成显式的。
和mem_fn相似,bind生成的可调用对象的第一个实参可以是string的指针,也可以是string的引用。
auto f = bind(&string::empty, _1);
f(*svec.begin());
f(&svec[0]);