You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于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这种语法(也就是代码里折叠表达式做的事情)时,成员指针会和具体的对象实例结合:
    1. 先拿到root指向的那个Node实例的起始内存地址。
    2. 加上成员指针left存储的偏移量,计算出这个具体实例中left成员的内存地址。
    3. 最后解引用这个地址,得到该成员的实际值(也就是指向另一个Node的指针)。

结合代码里的Traverse函数看效果

代码里的Traverse用了C++17的折叠表达式(np ->* ... ->* paths),它会把传入的成员指针依次“链式”应用到对象上。比如Traverse(root, left, right)的执行过程是:

  1. 先把root和left结合:root->*left,得到root实例的left成员(指向value=1的Node)。
  2. 再把这个结果和right结合:(root->*left)->*right,得到那个Node的right成员(指向value=2的Node)。
  3. 最后返回这个指针,所以cout输出的就是2。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 10:27:58