【问题标题】:(*it)->method() vs (**it).method(*it)->method() 与 (**it).method
【发布时间】:2025-12-05 06:05:03
【问题描述】:

当迭代指针的向量(或其他容器)时,使用:

for (it = v.begin(); it != v.end(); ++it) {
    (*it)->method();
}

for (it = v.begin(); it != v.end(); ++it) {
    (**it).method();
}

【问题讨论】:

  • 我看不出一个比另一个有什么特别的优势(无论如何都无法阅读)。
  • 选择一个更漂亮的。
  • 为什么会有一个指针向量?谁拥有指针(而不是向量)。如果您使用 boost::ptr_vector ,它会获取指针的所有权,但也会将其成员公开为对对象(而不是指针)的引用,因此不需要这种双重取消引用。
  • 每个 c++ 问题都会在某些时候带来提升:)。我相信有充分的理由。在这份工作中,我没有可用的提升。

标签: c++ vector iterator dot-operator


【解决方案1】:

在 C 语言中,没有区别。但是,在 C++ 中,-> 运算符可以重载,而成员选择 . 运算符则不能。

所以在(*foo)->bar *foo 中可以指定一个充当智能指针的类对象,但如果foo 是标准C++ 指针容器的迭代器,则不会发生这种情况,这意味着*foo计算结果为指针。

(**foo).bar 中,**foo 必须是一个类对象,其成员名为bar(可访问)。

一元*也可以被重载(这就是迭代器foo,一个类对象,返回它引用的对象的方式)。

换句话说,表达式可以在含义上有所不同,但如果*foo 是指向类/结构的指针,则适用从C 语言继承的等价:(*ptr).member 等价到ptr->member

【讨论】:

  • * 运算符也可以被重载,那么这有什么不同(除了使用不同的运算符并因此可能被重载的事实)。当然,有人可能会认为以不同方式重载这些运算符是个好主意,但这显然是一个非常糟糕的主意。
  • 任何重载-> 而不重载* 以使用法在语义上等效的人都会遇到很多麻烦(即他们疯了)。另请注意,OP 指出容器包含指针。
  • 人们能做和不能做的事不在这里也不在那里。无需诊断的可证明正确的 C++ 程序可能在同一类中具有语义不兼容的 *-> 重载。
  • 也仍然与问题无关,因为它与指针有关。此外,该网站的重点是要提供帮助,不要试图说出 99.999% 的时间都不适用的不敬的极端案例。
  • 感谢 Loki 巧妙、建设性的批评,我对(已经接受的)答案做了一些改进。
【解决方案2】:

它们是等价的:定义的标准(用于指针)

5.2.5 类成员访问 [expr.ref]
2:对于第一个选项(点),第一个表达式应具有完整的类类型。对于第二个选项(箭头),第一个表达式应具有指向完整类类型的指针。 表达式E1->E2被转换为等价形式(*(E1)).E2; 5.2.5 的其余部分将仅处理第一个选项(点)。65 在任何一种情况下,id 表达式都应命名该类或其基类之一的成员。

类和覆盖 -> 和 * 操作符在这里不相关,因为容器包含指针。

因此:

(*it)->method();

// Is equivelent to:

(*((*it))).method();

// This is equivelent too:

(**it).method(); // thus both expressions are identical in this context.

【讨论】:

  • 在第一个“等价于”是(*((*it))).method();(*(*it)).method(); 不同吗? IE。括号有什么作用?
  • @Ross:不,我只是在准确地达到标准。其中E1(*it)表示。
【解决方案3】:

没有。 -> 只是说将结构作为指针访问。 . 就像它只是一个结构一样。它是句法的 - 在功能方面没有区别。

编辑:你可以重载-> 让它做一些不同的事情,尽管假设你不这样做是一样的。不知道你为什么要这样做,但如果你这样做了,那么你必须使用额外的 * 再次取消引用你的结构。

【讨论】:

  • 嗯,是的,有区别。 operator-> 可以重载,编译器将链接到 operator-> 的调用以获取对象。
【解决方案4】:

它们是一样的。例如,您可以根据使用的编码约定使用其中一种。

->* 运算符可以重载,而 . 运算符不能。

【讨论】:

    【解决方案5】:

    在 Ubuntu 12.04 上,列表迭代器以下列方式实现:

    template<typename _Tp>
    struct _List_iterator
    {
    
      // ...
      reference
      operator*() const
      { return static_cast<_Node*>(_M_node)->_M_data; }
    
      pointer
      operator->() const
      { return std::__addressof(static_cast<_Node*>(_M_node)->_M_data); }
    
      // ...
    

    【讨论】: