一、先记结论

  • .:用于对象本体或对象引用访问成员
  • ->:用于指针访问成员
  • p->x 等价于 (*p).x

只要看变量类型就能快速判断:

  • 类型是 TT&,用 .
  • 类型是 T*,用 ->

二、本质区别

1. 点号 .

a.b 表示“变量 a 本身就是对象(或引用),直接访问成员 b”。

struct Node {
    int val;
};

Node n;
n.val = 10;

Node& ref = n;
ref.val = 20;

2. 箭头 ->

p->b 表示“p 是指针,先找到它指向的对象,再访问成员 b”。

Node n;
Node* p = &n;
p->val = 30;

这句完全等价于:

(*p).val = 30;

注意 (*p).val 里的括号不能省,因为 . 的优先级高于 *

三、为什么容易混淆

日常写题时会同时出现“对象”和“对象指针”,特别是链表、二叉树、图等数据结构题。

例如:

  • Node node; 是对象,写 node.val
  • Node* node; 是指针,写 node->val

名称都叫 node,但类型不同,访问符号就不同。

四、class 和 struct 有关系吗

.-> 这件事上,classstruct 没有区别。

二者都支持这两种访问方式,是否能用只由“你手里是对象还是指针”决定。

常见差异只是默认权限:

  • class 默认 private
  • struct 默认 public

五、典型使用场景

1. 用 . 的场景

  • 栈上对象
  • 作为成员的对象
  • 引用变量
Student s;
s.name = "Tom";

Student& rs = s;
rs.name = "Jerry";

2. 用 -> 的场景

  • 裸指针(T*
  • 动态分配对象
  • 链表/树节点指针
  • 智能指针(unique_ptrshared_ptr
auto p = std::make_unique<Student>();
p->name = "Alice";

六、常见错误

错误 1:对指针使用 .

Node* p;
p.val = 1; // 错误

错误 2:对对象使用 ->

Node n;
n->val = 1; // 错误

错误 3:忘记空指针检查

if (p != nullptr) {
    cout << p->val << '\n';
}

七、示例:2236. 判断根结点是否等于子结点之和

给你一个 二叉树 的根结点 root,该二叉树由恰好 3 个结点组成:根结点、左子结点和右子结点。

题目代码:

class Solution {
public:
    bool checkTree(TreeNode* root) {
        return root->val == root->left->val + root->right->val;
    }
};

这里 root 的类型是 TreeNode*,所以要写:

  • root->val
  • root->left
  • root->right

如果写 root.val 会报错,因为 root 不是对象本体,而是对象地址。

八、速查表

变量类型 写法 示例
T . obj.x
T& . ref.x
T* -> ptr->x
智能指针 -> sp->x

九、总结

判断原则只有一条:看类型。

  • 对象/引用:用 .
  • 指针:用 ->

-> 在脑中翻译成“先解引用再点号”,很多细节会立刻清晰。