关于C++中`&Node::left`成员指针语法及内存定位逻辑的技术问询
关于C++中
&Node::left成员指针语法及内存定位逻辑的技术问询 嘿,我来给你掰扯清楚这两行代码的门道,还有你疑惑的内存定位问题~
先把你贴的完整代码放出来方便对照:
#include <iostream> struct Node { int value; Node* left; Node* right; Node(int i = 0): value(i), left(nullptr), right(nullptr) {} }; auto left = &Node::left; auto right = &Node::right; template<typename T, typename... TP> Node* Traverse(T np, TP... paths) { return (np ->* ... ->* paths); } int main() { Node* root = new Node{0}; root->left = new Node{1}; root->left->right = new Node{2}; Node* node = Traverse(root, left, right); std::cout << node->value << std::endl; }
先搞懂auto left = &Node::left;到底是什么
这行代码声明的不是普通指针,而是C++里的成员指针(pointer to member)。咱拆解一下:
&Node::left表示的是「Node类中名为left的成员的指针」,它的实际类型是Node* Node::*(翻译成人话:指向Node类的、类型为Node*的成员的指针)。用auto推导的话,left的类型就被自动确定成这个了,right同理。- 重点:这个成员指针不存储任何具体内存地址,它存的是这个成员在Node类实例中的偏移量——也就是从一个Node对象的起始地址,到left成员所在位置的字节数,这个偏移量是编译器在编译期就确定死的。
为什么它能正确定位到root实例里的left成员?
你疑惑的点是root是内存里的一个具体实例,为啥left这个成员指针能找到它的left成员?核心原因有两个:
- 所有Node类的实例,内存布局都是完全一致的。比如Node是结构体,编译器会按成员声明的顺序排布内存:先放
value(int类型,占4字节,假设64位系统),然后是left(Node*,占8字节),最后是right(Node*,8字节)。所以left成员相对于Node实例起始地址的偏移量,不管哪个实例都是固定的(比如这里是4字节,从value的末尾开始)。 - 当你用
root->*left这种语法(也就是代码里折叠表达式做的事情)时,成员指针会和具体的对象实例结合:- 先拿到root指向的那个Node实例的起始内存地址。
- 加上成员指针left存储的偏移量,计算出这个具体实例中left成员的内存地址。
- 最后解引用这个地址,得到该成员的实际值(也就是指向另一个Node的指针)。
结合代码里的Traverse函数看效果
代码里的Traverse用了C++17的折叠表达式(np ->* ... ->* paths),它会把传入的成员指针依次“链式”应用到对象上。比如Traverse(root, left, right)的执行过程是:
- 先把root和left结合:
root->*left,得到root实例的left成员(指向value=1的Node)。 - 再把这个结果和right结合:
(root->*left)->*right,得到那个Node的right成员(指向value=2的Node)。 - 最后返回这个指针,所以cout输出的就是2。
内容来源于stack exchange
相关产品推荐
相关产品推荐

