定义
指向类非静态static成员的指针。普通的指针的,都是指向类对象的指针。
静态成员不属于类,指向静态成员的指针就是普通指针。
类成员指针类型包含两部分:类类型和成员类型,即指向哪个类的哪个成员。
由于成员类型的不同,可以分为:
- 数据成员指针,指向数据成员。
- 成员函数指针,指向函数成员。
数据成员指针
class T {
private:
int fuck;
int shit;
};
int main(){
// ****************************************************
// 声明方式(数据成员指针)
// ****************************************************
// 声明方式一
int T::*pData; // pData是指向类T的int类型成员的指针。
pData = &T::fuck; // pData指向fuck。
// 声明方式二,C++新标准
auto pData = &T::fuck; // 同上
// 声明方式三
decltype(&T::fuck) pData = &T::fuck; // 同上
// 声明方式四,using或者typedef
using MemPtr = int T::*; // 类型别名,类T的int型成员
MemPtr pData = &T::fuck; // 同上
// ****************************************************
// 使用方式(数据成员指针),获得类成员,进而获得对象的该成员。
// ****************************************************
T t, *pt = &t;
auto s = t.*pData; // *pData解引用获得类成员,然后通过对象访问t.,获得对象的该数据成员
s = pt->*pData; // 指针形式。
}
返回数据成员指针的函数:
class T {
private:
int fuck;
int shit;
};
int T::* fuck(){ // 返回数据成员指针,指向类T的int类型成员。
return &T::fuck; // 返回fuck成员
}
int T::*shit(){ // 同上,别搞错了。
return &T::shit; // 返回shit成员
}
int main(){
auto p1 = fuck(); // 数据成员指针,指向fuck成员
auto p2 = shit(); // 数据成员指针,指向shit成员
int T::* p1 = fuck(); // 同上
int T::* p2 = shit(); // 同上
decltype(fuck()) p1 = fuck(); // 同上
decltype(shit()) p2 = shit(); // 同上
T t;
std::cout << t.*p1 << std::endl; // 打印fuck成员
std::cout << t.*p2 << std::endl; // 打印shit成员
}
成员函数指针
指向成员函数的类成员指针。不同于函数指针,这不是可调用对象,必须要先绑定到类对象。
class T {
public:
void fuck() const{
std::cout << "Base::fuck" << std::endl;
}
}
int main(){
// ****************************************************
// 声明方式(成员函数指针)
// ****************************************************
// 声明方式1
void (T::*pFunc)() const; // pFunc是类成员函数指针,指向的函数签名形式void()const
pFunc = &T::fuck;
pFunc = T::fuck; // 错误,在成员函数和指针之间不存在自动转换规则
// 声明方式2
auto pFunc = &T::fuck; // 同上
// 声明方式3
decltype(&T::fuck) pFunc = &T::fuck;
// 声明方式4,using或者typedef
using FuncPtr = void (T::*) const;
FuncPtr pFunc = &T::fuck;
// ****************************************************
// 使用方式(成员函数指针)
// ****************************************************
T t, *pt = &t;
(t.*pFunc)(); // 执行func函数,别漏了前面的括号。
(t->*pFunc)(); // 执行func函数
}
实践例子
移动光标。
int main(){
Screen myScreen;
myScreen.move(Screen::HOME); // 调用myScreen.home
myScreen.move(Screen::DOWN); // 调用myScreen.down
}
class Screen {
public:
using Action = Screen& (Screen::*)();
enum Directions { HOME, FORWARD, BACK, UP, DOWN };
public:
//其他接口和 实现成员 与之前一致
Screen& home(); //光标移动函数
Screen& forward();
Screen& back();
Screen& up();
Screen& down();
Screen& move(Directions cm){
//运行this对象中索引值为cm的元素
return (this->*Menu[cm])(); //Menu[cm]指向一个成员函数
}
private:
static Action Menu[] = { //函数表
&Screen::home,
&Screen::forward,
&Screen::back,
&Screen::up,
&Screen::down,
}
};
变成可调用对象
成员函数指针本身不是可调用对象,必须利用.或者->运算符先绑定到对象上。以下代码是错误示例:
auto fp = &string::empty; // fp指向string的empty函数
// 错误,fp不是可调用对象,它是一个类成员函数指针。
find_if(svec.begin(), svec.end(), fp);
思路
1、可调用对象,就是重载调用运算符
2、类对象初始化的时候传入类成员函数指针
3、调用运算符形参传入类对象,接着成员函数指针绑定到这个类对象上,然后执行。
class T{
......
bool operator()(const string& str){...} // 传入成员函数指针绑定的类对象。
bool operator()(const string* str){...} // 指针版本
......
};
string str = "asdfasdf";
T t(&string::empty); // 初始化的时候传入成员函数指针
t(str); // 执行的时候,传入类对象。内部则绑定成员函数指针并执行。
t(&str); // 指针版本
完整代码如下:
class T
{
public:
using Action = bool ( string::* )( ) const; // 指向类string的这样的成员函数bool()
public:
T( Action action ) : _action( action ), _pStr(nullptr) {}
bool operator()( const string &str ) { // 引用版本
return ( str.*_action )();
}
bool operator()(const string* pStr){ // 指针版本
return ((*pStr).*_action)();
}
private:
string* _pStr;
Action _action;
};
int main()
{
std::vector<string> svec = { "abc", "bca", "A" };
T t( &string::empty );
auto findIt = svec.end();
if( ( findIt = find_if( svec.begin(), svec.end(), t ) ) != svec.end() ) {
std::cout << "find it." << std::endl;
}
return 0;
}
方法一:std::function
std::function已将上述原理过程进行封装,我们直接拿来用即可。
// 初始化的时候,定好成员函数指针
// <>尖括号里的特例化了重载调用运算符的签名bool(const string&)
function<bool(const string&)> fcn = &string::empty;
// 在find_if内部以这种方式调用函数对象fcn:bool ret = fcn(*it); *it返回一个元素的引用。
find_if(svec.begin(), svec.end(), fcn);
方法二:mem_fn
上面的std::function还有点麻烦,就是我们需要显式声明成员函数的签名,我们完全可以让编译器完成这个功能,标准库函数mem_fn即可完成这个需求。
mem_fn直接返回一个可调用对象。
vector<string> svec = {......};
// shit直接就是一个可调用对象
auto shit = mem_fn(&string::empty);
// 自动推断出:是接受一个指向string的引用,然后使用.*调用empty
find_if(svec.begin(), svec.end(), shit);
方法三:std::bind
vector<string> svec = {......};
auto f = bind(&string::empty, _1);
find_if(svec.begin(), svec.end(), f);
f(*svec.begin()); // 引用版本
f(&svec[0]); // 指针版本