定义
指向类非静态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或者typedefusing 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(){// ****************************************************// 声明方式(成员函数指针)// ****************************************************// 声明方式1void (T::*pFunc)() const; // pFunc是类成员函数指针,指向的函数签名形式void()constpFunc = &T::fuck;pFunc = T::fuck; // 错误,在成员函数和指针之间不存在自动转换规则// 声明方式2auto pFunc = &T::fuck; // 同上// 声明方式3decltype(&T::fuck) pFunc = &T::fuck;// 声明方式4,using或者typedefusing 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.homemyScreen.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的引用,然后使用.*调用emptyfind_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]); // 指针版本
