C++ 成员指针(Pointer to Member)完全指南


📘 C++ 成员指针(Pointer to Member)完全指南

核心思想 :成员指针不是普通指针,它指向的是"类中某个成员的位置(偏移量)",必须与具体对象结合才能使用


一、什么是成员指针?

C++ 允许你创建两种成员指针:

  1. 指向成员变量的指针
  2. 指向成员函数的指针

它们不存储内存地址 ,而是存储在类布局中的偏移信息 ,因此不能脱离对象单独使用


二、语法与声明

✅ 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.*ptrpObj->*ptr (obj.*func)()
*ptr 含义 不是解引用,是 .* 运算符的一部分 不能单独使用
&A 是否合法 ❌ 非法!类名不能取地址 &obj 才合法
现代替代方案 用 lambda + 捕获 [&]{ obj.f(); }

相关推荐
后端AI实验室17 小时前
我把同一个需求分别交给初级程序员、高级程序员和AI,结果让我沉默了
java·ai
xlp666hub17 小时前
Leetcode第二题:用 C++ 解决字母异位词分组
c++·leetcode
sTone8737517 小时前
web后端开发概念: VO 和 PO
java·后端·架构
用户7268761033717 小时前
解放双手的健身助手:基于 Rokid AR 眼镜的运动计时应用
算法
Wect17 小时前
LeetCode 17. 电话号码的字母组合:回溯算法入门实战
前端·算法·typescript
不想写代码的星星18 小时前
static 关键字:从 C 到 C++,一篇文章彻底搞懂它的“七十二变”
c++
SimonKing18 小时前
JetBrains+Qoder变身Agentic 编码平台,媲美Cursor、Trae等AI编程平台
java·后端·程序员
Seven9719 小时前
NIO:解开非阻塞I/O高并发编程的秘密
java
华仔啊1 天前
千万别给数据库字段加默认值 null!真的会出问题
java·数据库·后端
老赵全栈实战1 天前
【每日一技MyBatis trim标签核心用法
java·mybatis·orm