📘 C++ 成员指针(Pointer to Member)完全指南
核心思想 :成员指针不是普通指针,它指向的是"类中某个成员的位置(偏移量)",必须与具体对象结合才能使用。
一、什么是成员指针?
C++ 允许你创建两种成员指针:
- 指向成员变量的指针
- 指向成员函数的指针
它们不存储内存地址 ,而是存储在类布局中的偏移信息 ,因此不能脱离对象单独使用。
二、语法与声明
✅ 1. 指向成员变量的指针
cpp
类型 类名::* 指针名;
示例:
cpp
class A {
public:
int x;
double y;
};
int A::* ptrX = &A::x; // 指向 A 中的 int 成员 x
double A::* ptrY = &A::y; // 指向 A 中的 double 成员 y
✅ 2. 指向成员函数的指针
cpp
返回类型 (类名::*指针名)(参数列表);
示例:
cpp
class A {
public:
void print();
int add(int a, int b);
};
void (A::*func1)() = &A::print; // 无参无返回
int (A::*func2)(int, int) = &A::add; // 有参有返回
🔑 关键:
- 必须使用
&类名::成员名获取成员指针- 不能写
&成员名或&A(类名不能取地址)
三、如何使用?------必须绑定对象!
成员指针不能单独调用或访问,必须通过以下两个专用运算符:
| 运算符 | 用途 | 示例 |
|---|---|---|
obj.*ptr |
通过对象访问成员 | obj.*ptrX |
pObj->*ptr |
通过对象指针访问成员 | pObj->*ptrX |
🧪 完整示例:
cpp
A obj;
A* pObj = &obj;
// 访问成员变量
obj.*ptrX = 42; // 等价于 obj.x = 42;
cout << pObj->*ptrY; // 等价于 cout << obj.y;
// 调用成员函数
(obj.*func1)(); // 调用 obj.print()
int r = (pObj->*func2)(3,4); // 调用 obj.add(3,4)
⚠️ 注意括号!
❌
obj.*func1()→ 先调用func1()(错误)✅
(obj.*func1)()→ 先组合,再调用
四、关于 *ptr 的真相:它不是"解引用取值"!
这是最常见的误解!
❌ 错误理解:
"
*ptr是解引用,得到成员本身"
✅ 正确理解:
- 在
obj.*ptr中,.*是一个整体运算符(读作 "object dot star") *ptr不能独立存在,也不是传统意义上的"解引用"- 成员指针没有"值"的概念,只有"如何在一个对象上定位成员"的信息
💡 类比:
ptrX就像一张"户型图上的标记:'主卧在此'"
obj是一栋具体的楼
obj.*ptrX= "在这栋楼里,找到主卧"
五、常见错误与澄清
| 错误写法 | 为什么错 | 正确做法 |
|---|---|---|
*varPtr = &A; |
1. &A 非法(类名不能取地址)2. *varPtr 不能单独赋值 |
obj.*varPtr = 100; |
&A |
类是类型,不是对象,无地址 | &obj(对对象取地址) |
funcPtr() |
成员函数指针不能直接调用 | (obj.*funcPtr)() |
*funcPtr |
无实际意义,不能调用 | 必须结合对象使用 |
int* p = &A::x; |
成员指针 ≠ 普通指针,类型不匹配 | int A::* p = &A::x; |
六、&Class::member 到底是什么?
&A::x:获取 成员变量x在类A中的偏移信息&A::func:获取 成员函数func在类A中的入口偏移- 它们不是内存地址,而是编译器内部用于定位成员的元数据
- 类型分别为:
int A::*void (A::*)()
✅ 所以
&Class::member是获取成员指针的唯一合法方式
七、现代 C++ 建议:优先使用 Lambda
虽然成员指针是标准特性,但日常开发几乎不需要直接使用 。推荐用 lambda 表达式替代:
cpp
A obj;
// 替代成员变量指针
auto getX = [&]() { return obj.x; };
auto setX = [&](int v) { obj.x = v; };
// 替代成员函数指针
auto callPrint = [&]() { obj.print(); };
// 可直接传给 std::function
std::function<void()> f = callPrint;
✅ 优势:
- 语法简洁
- 自动处理对象生命周期(注意引用捕获)
- 编译器更容易优化
- 避免
.*/->*的复杂语法
八、总结:一张表掌握全部
| 概念 | 说明 | 示例 |
|---|---|---|
| 成员指针类型 | 指向类成员的"偏移量",非内存地址 | int A::*, void (A::*)() |
| 获取方式 | 必须用 &类名::成员 |
&A::x, &A::print |
| 使用方式 | 必须结合对象:obj.*ptr 或 pObj->*ptr |
(obj.*func)() |
*ptr 含义 |
不是解引用,是 .* 运算符的一部分 |
不能单独使用 |
&A 是否合法 |
❌ 非法!类名不能取地址 | ✅ &obj 才合法 |
| 现代替代方案 | 用 lambda + 捕获 | [&]{ obj.f(); } |