成员变量指针是C++中指向类非静态成员变量的特殊指针,不存储内存地址而存储该成员在类对象中的字节偏移量;必须结合具体对象(用.*)或对象指针(用->*)才能访问实际值,声明格式为类型 类名::*指针名,且不可指向静态成员或位域。

什么是成员变量指针,和普通指针有啥区别
成员变量指针不是直接存地址的值,它存的是“该成员在类对象内存布局中的偏移量”。这意味着你不能单独使用它,必须配合一个具体对象(或指针)才能访问到实际值。比如 &MyClass::x 得到的不是一个内存地址,而是一个编译期确定的偏移量;只有写成 obj.<em>ptr</em> 或 ptr_obj->ptr 才能真正取值。
常见错误现象:
- 直接对成员指针做
cout << ptr,输出是类似1或8的整数(偏移字节数),不是地址 - 尝试用
new分配成员指针、或赋值为nullptr(合法但无意义,nullptr不能用于.<em></em>或->) - 忘记类型声明里必须带类名限定:
int MyClass::*,漏掉MyClass::会编译失败
怎么声明和初始化成员变量指针
声明格式固定:类型 类名::* 指针名
例如:
struct Person { int age; std::string name; };
int Person::* p_age = &Person::age;
std::string Person::* p_name = &Person::name;注意点:
- 必须用取地址符
&初始化,且右边必须是类名::成员名形式 - 静态成员变量不能用成员指针指向(它是全局地址,直接用普通指针就行)
- const 成员变量可以被指向,但通过指针访问时仍受 const 限制(如 const int Person::* 只能绑定到 const 成员)
- 不能指向 bit-field(位域),编译器会报错
怎么用成员指针访问对象里的值
必须搭配对象实例或对象指针,用两个专用操作符:
立即学习“C++免费学习笔记(深入)”;
-
obj.*ptr:用于对象本身(非指针) -
ptr_obj->*ptr:用于对象指针
示例:
Person p{25, "Alice"};
std::cout << p.*p_age << "\n"; // 输出 25
std::cout << (p.*p_name).c_str() << "\n"; // 输出 Alice
<p>Person<em> pp = &p;<br />
std::cout << pp-></em>p_age << "\n"; // 同样输出 25容易踩的坑:
- 操作符优先级低,
p.<em>p_age++</em>实际是(p.p_age)++,不是移动指针(成员指针本身不可增减) - 如果成员是 private,且当前作用域无访问权限,即使用了成员指针也无法访问(访问控制在编译期检查,不因指针绕过)
- 多重继承下,某些编译器对虚基类成员的偏移计算更复杂,但语法不变,无需额外处理
成员指针在模板和泛型代码里怎么用
它常用于泛型容器或反射模拟,比如写一个通用的字段提取函数:
template<typename T, typename M>
M get_field(const T& obj, M T::* ptr) {
return obj.*ptr;
}
<p>// 使用:<br />
int x = get_field(p, &Person::age);关键约束:
- 模板参数
M T::<em></em>必须完整写出,不能简写成M::或T::* - 无法用 auto 推导成员指针类型(C++17 前),C++17 起支持
auto p = &Person::age;,但类型仍是int Person::* - 成员指针不能作为非类型模板参数(NTTP),直到 C++20 才允许(且仅限于具有静态存储期的成员)
成员指针本身不占运行时开销,但每次解引用都要加一次偏移计算——现代 CPU 上基本可忽略,但别在 hot path 里反复算同一偏移(比如循环内重复写 obj.*ptr 而不缓存)。
真正容易被忽略的是:它不能跨继承体系隐式转换,哪怕子类和父类字段同名同类型,&Derived::x 和 &Base::x 是不同类型,不能混用。


















