指向类成员的指针(.*)(->*)

要点

  1. 指针是有类型的,这点和「通常的指针」一样,指向int的指针也只能指向int类型的数据
  2. 相对于「通常的指针」,增加了对指向对象的限制 —— 指向的对象限定为某个class中的成员
  3. 这种指针不可以单独使用,需要配合一个具体存在的对象才有意义
#include <bits/stdc++.h>

using namespace std;

class A
{
public:
	int para1;
	int para2;
	int callback(int n)
	{
		return 2 * n;
	}

	void display()
	{
		cout << para1 << " " << para2 << endl;
	}
};

int main()
{
	A a;
	A *pa = &a;
	a.para1 = 1;
	a.para2 = 2;
	a.display(); /* 输出:1 2 */

	/* pInt是指向int类型数据的指针,且仅限于class A中的int成员 */
	int A::*pInt;
	/* pInt指向class A中的para1成员 */
	pInt = &A::para1;
	/* 利用专用的运算符「->*」提取对象a中的para1成员,  可以换另一种写法「a.*pInt = 123;」 */
	pa->*pInt = 123;
	a.display(); /* 输出:123 2 */

	/* pInt改为指向class A中的para2成员 */
	pInt = &A::para2;
	/* 利用专用的运算符「->*」提取对象a中的para2成员,  可以换另一种写法「a.*pInt = 456;」 */
	pa->*pInt = 456;
	a.display(); /* 输出:123 456 */

	/* pfunc是函数指针,函数参数为1个int,返回值为int,该指针仅限指向class A中相应类型的成员 */
	int (A::*pfunc)(int n) = &A::callback;
	cout << (pa->*pfunc)(7) << endl; /* 输出:14 */
									 //等同于 cout << (a.*pfunc)(7) << endl;
}

纠结的演化过程

习惯了常规的指针,这种特殊的,指向类成员的指针确实有点诡异。但可以按照下面这样的演化过程来理解这种指针。

step1:指向int型的指针

int *pInt;

step2:指向int型的指针+限制为class A的成员

int A::*pInt;

step3:指向具体的class A的成员

pInt = &A::para1;

如此看,等号右边的&已经不同于通常意义的「取地址」符号,毕竟A::para1也不是一个具体的数据对象
step4:将指针与具体的class A的对象结合,这样就能干点什么了

pa->*pInt = 123;
a.*pInt = 123;

上述操作也分别等价于

pa->para1 = 123;
a.para1 = 123;

对比上述2种赋值para1的方式,可以有个初步的结论:*pInt字面上等价于para1

此时再回顾step3中莫名其妙的&符号,似乎也有了一点点道理,毕竟无论怎样用到pInt,前面都必须带个*也就是必须以*pInt的形式出现,这个解引用的*,正好可以和取地址符号&相互抵消,换句话说,*pInt作为一个整体,成为了「class A中的para1成员」的代名词。

一点疑惑

既然*pInt成为了一个整体,似乎其中的*有点多余了,直接将pInt声明为「class A中的para1成员的代名词」不就可以了?比如说像这样

int A::Represent_para1;
Represent_para1 = A::para1;
a.Represent_para1 = 123;

实践证明这样是不行的,vscode中报错提示:不允许使用限定名

暂时没有想明白为什么不允许这样,不过直观上看,这样做确实会有点怪怪的——似乎声明了Represent_para1是class A的一个成员。
暂且先接受这个设定吧,之后弄明白了再来补充。

实战用途

下面的代码中,class A提供了1个公用的接口GetResult,该接口根据传入的参数不同,控制调用不同的内部函数,并返回相应的数值。

#include <bits/stdc++.h>

using namespace std;

class A
{
private:
	int func1(int n) { return 2; }
	int func2(int n) { return 4; }
	int func3(int n) { return 6; }

	/* 关注点1 */
	vector<pair<int, int (A::*)(int)>> vecList = {
		{1, &func1},
		{2, &func2},
		{3, &func3},
	};

public:
	int GetResult(int n)
	{
		for (auto iter = vecList.begin(); iter != vecList.end(); iter++)
		{
			if (iter->first == n)
			{
				return (this->*(iter->second))(n); /* 关注点2 */
			}
		}

		return 0;
	}
};

int main()
{
	A a;
	cout << a.GetResult(0) << endl; /* 输出:0 */
	cout << a.GetResult(1) << endl; /* 输出:2 */
	cout << a.GetResult(2) << endl; /* 输出:4 */
	cout << a.GetResult(3) << endl; /* 输出:6 */
}

需要注意的有两点

关注点1

vecList是元素为pair的数组,其第二个成员类型就是上面提到的「指向类成员的指针」,这里并不能用普通的函数指针代替(类型不匹配),因为func1、func2、func3都是class A的成员函数,这与其它普通的函数身份是不一样的。

仿照前面纠结的演化过程的推导,不难看出int (A::*)(int)的“原始形态”就是 int (*)(int),即「返回值为int,参数为1个int的函数指针」,只不过加上了限定符A::之后,这个指针被限制只能指向class A的成员。

关注点2

在通过「指向类成员的指针」调用相应的「成员函数」时,首先通过iter->second提取到了相应的指针。根据前面的讨论可知,这个指针只是表明了「指向class A的具体的某个成员(函数)」,只依据这个指针是无法完成调用的,这个指针必须依附在一个实际存在的class A的实例上才可以使用,显然,最合适的人选就是发起这次调用的a,最终的调用形式即为(this->*(iter->second))(n),当然,也可以换个形式:((*this).*(iter->second))(n)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值