指向类成员的指针
C++扩展了指针在类中的使用,使其可以指向类成员(数据成员和函数成员),这种行为是类层面的,而不是对象层面的。
C pointer 回顾
在 C 语言中,指针,既可以指向对象,也可以指向函数。给语言的便利操作带来了极大的便利
#include <iostream>
using namespace std;
void func(int a)
{
cout<<a<<endl;
}
int main()
{
int a = 100;
int *p = &a;
cout<<*p<<endl;
void (*pf)(int) = func;
pf(10);
return 0;
}
运行结果为:
指向类数据成员的指针
指向类数据成员的指针,是类层面的指针,而不是对象层面的指针。
语法
指向非静态数据成员的指针在定义时必须和类相关联,
在使用时必须和具体的对象相关联
由于类不是运行时存在的对象。因此,在使用这类指针时,需要首先指定类的一个对象,然后,通过对象来引用指针所指向的成员。
应用
指向类数据成员的指针,跟普通意义上的指针用法有很大的差别。定义格式上,必须有类别限定 string Student::*pn,初始化上,也必须有类名限定&Student::name,并且定义的指针,只有通过具体的对象或对象指针才可以调用。下面进行举例说明。
我们在这里强调,是定义一个指针指向类的成员,而不是指向对象的成员。
因为类内的私有数据成员不允许外部访问,所以不允许在类外定义一个指针指向类内的私有数据成员。
下面代码中注释部分及就是举例使用类外指针指向类内的私有成员。
#include <iostream>
using namespace std;
class Student
{
public:
Student(string n, int nu) :name(n), num(nu) {}
string name; //数据成员先设置为public
int num;
};
int main()
{
Student s("zhangsi", 100);
Student* ps = &s;
Student ss("wangwu", 666);
Student* pss = &ss;
// string *tmp = &s.name;// 跟封装相违背
// cout<<*tmp<<endl;
string Student:: * psn = &Student::name; //定义
//使用还需要和具体的对象产生关系
//我们已经用过的访问方式
//s.name;
//ps->name;
//换一种访问方式
cout << s.*psn << endl;
cout << ps->*psn << endl;
cout << ss.*psn << endl;
cout << pss->*psn << endl;
return 0;
}
运行结果为:
但是呢,其实上面实现的意义不是很大,因为我们定义类的时候,成员变量只是私有的,上面我们把私有变量进行了释放才能实现。所以接下来我们看指向类内函数的指针。
指向类成员函数的指针
语法
定义一个指向非静态成员函数的指针必须在 三个方面与其指向的成员函数保持一致:参数列表要相同、返回类型要相同、所属的类型(类名)要相同。
由于类不是运行时存在的对象。因此,在使用这类指针时,需要首先指定类的一个对象,然后,通过对象来引用指针所指向的成员。
#include <iostream>
using namespace std;
class Student
{
public:
Student(string n, int nu) :name(n), num(nu) {}
void dis(int idx)
{
cout <<"idx "<< idx << " name " <<name<<" num "<<num << endl;
}
private:
string name;
int num;
};
int main()
{
void (Student ::*pdis)(int idx) = &Student::dis;
Student s("zhangsan", 300);
Student ss("zhangsi", 666);
(s.*pdis)(1);
(ss.*pdis)(2);
Student* ps = &s;
Student* pss = &ss;
(ps->*pdis)(3);
(pss->*pdis)(4);
return 0;
}
运行结果为:
指针或者偏移量
指向类成员的指针,具有指针的形而不具体指针的实质,或者,确切意义上说,不指是指针,指向类成员的指针,本质存放的不是地址,存放的偏移量。
指向类静态成员的指针
指向静态成员的指针的定义和使用与普通指针相同,在定义时无须和类相关联,在使用时也无须和具体的对象相关联。
语法
应用
也普通函数指针,唯一的区别就是,在初始化时,&A::data,要加上类名限定。
#include <iostream>
using namespace std;
#include <iostream>
using namespace std;
class A
{
public:
static void dis();
static int data;
};
void A::dis()
{
cout<<data<<endl;
}
int A::data = 100;
int main()
{
int *pa = &A::data;
cout<<*pa<<endl;
void (*pf)() = A::dis;
pf();
return 0;
}
应用:更加统一的接口
#include <iostream>
using namespace std;
struct Point
{
int add(int x, int y)
{
return x + y;
}
int minus(int x, int y)
{
return x - y;
}
int mul(int x, int y)
{
return x * y;
}
int div(int x,int y)
{
return x / y;
}
};
int oper(Point& p, int(Point::* pf)(int x, int y), int x, int y)
{
return (p.*pf)(x, y);
}
typedef int(Point::* PF)(int x, int y);
int main()
{
Point p;
PF pf = &Point::add;
cout << oper(p, pf, 1, 2) << endl;
pf = &Point::mul;
cout << oper(p, pf, 1, 2) << endl;
pf = &Point::minus;
cout << oper(p, pf, 1, 2) << endl;
pf = &Point::div;
cout << oper(p, pf, 1, 2) << endl;
}
运行结果为:
应用:更加隐藏的接口
#include <iostream>
using namespace std;
class Game
{
public:
Game()
{
pf[0] = &Game::f;
pf[1] = &Game::g;
pf[2] = &Game::h;
pf[3] = &Game::l;
}
void select(int i)
{
if (i>= 0 && i <= 3)
{
(this->*pf[i])(i);
}
}
private:
void f(int idx) { cout << "void f(int idx)" << idx<< endl; }
void g(int idx) { cout << "void g(int idx)" << idx << endl; }
void h(int idx) { cout << "void h(int idx)" << idx << endl; }
void l(int idx) { cout << "void l(int idx)" << idx << endl; }
enum
{
nc = 4
};
void (Game::*pf[nc])(int idx);
};
int main()
{
Game g;
g.select(1);
g.select(2);
g.select(3);
return 0;
}
运行结果为: