【问题标题】:Overloading member access operators ->, .*重载成员访问运算符 ->, .*
【发布时间】:2012-02-05 08:43:37
【问题描述】:

我了解大多数运算符重载,但成员访问运算符 ->.*->* 等除外。

具体来说,传递给这些运算符函数的是什么,应该返回什么?

运算符函数(例如 operator->(...) )如何知道所引用的成员是什么?它可以知道吗?它甚至需要知道吗?

最后,是否有任何 const 需要考虑的因素?例如,当重载 operator[] 之类的东西时,通常你需要一个 const 和 non-const 版本。成员访问运算符是否需要 const 和非 const 版本?

【问题讨论】:

  • 我相信上面的 C++-Faq 涉及到上面 Q 中的所有问题。
  • const 和非const 版本的operator-> 不是必需的,但同时提供两者可能会有用。
  • @Als:FAQ 没有解释如何重载->*.*。事实上,它甚至没有提到他们!我觉得他们很少出现在常见问题解答中,但我很乐意将这个问题从常见问题解答中链接起来。 请不要因为FAQ的欺骗而关闭它!
  • @sbi,我完全没能从你的(真棒)常见问题解答中找到指向这个问题的链接,最后问了一个重复的问题。你能让它更明显吗? (如果已经很明显,请道歉)。

标签: c++ operator-overloading c++-faq


【解决方案1】:

->

这是唯一一个非常棘手的问题。它必须是一个非静态成员函数,并且不带任何参数。返回值用于执行成员查找。

如果返回值是另一个类类型的对象,而不是指针,那么后续的成员查找也由operator-> 函数处理。这称为“向下钻取行为”。语言将operator-> 调用链接在一起,直到最后一个返回指针。

struct client
    { int a; };

struct proxy {
    client *target;
    client *operator->() const
        { return target; }
};

struct proxy2 {
    proxy *target;
    proxy &operator->() const
        { return * target; }
};

void f() {
    client x = { 3 };
    proxy y = { & x };
    proxy2 z = { & y };

    std::cout << x.a << y->a << z->a; // print "333"
}

-&gt;*

这个比较棘手,因为它没有什么特别之处。 非重载 版本需要一个指向左侧类类型指针的对象和一个指向右侧成员类型指针的对象。但是当你重载它时,你可以接受任何你喜欢的参数并返回你想要的任何东西。它甚至不必是非静态成员。

换句话说,这只是一个普通的二元运算符,如+-/。另见:Are free operator->* overloads evil?

.*.

这些不能重载。当左侧是类类型时,已经有一个内置的含义。或许能够为左侧的指针定义它们有点意义,但语言设计委员会认为这会更令人困惑而不是有用。

重载-&gt;-&gt;*..* 只能填充表达式未定义的情况,它永远不会改变没有重载时有效的表达式的含义。

【讨论】:

  • 您最后的说法并不完全正确。例如,您可以重载 new 运算符,即使它在未重载时也是有效的。
  • @Matt 好吧,new 总是重载,或者重载规则并不真正适用于它(13.5/5:分配和释放函数,operator new,operator new[],operator delete和 operator delete[],在 3.7.4 中有完整的描述。除非在 3.7.4 中明确说明,否则本小节其余部分中的属性和限制不适用于它们。)但是重载一元 &amp; 或二元 &amp;&amp;||,,或添加 operator= 的重载,或为无范围的枚举类型重载几乎任何内容,都可以改变表达式的含义。澄清声明,谢谢!
【解决方案2】:

运算符 -> 是特殊的。

“它有额外的、非典型的约束:它必须返回一个对象(或对一个对象的引用),该对象也有一个指针解引用运算符,或者它必须返回一个可以用来选择指针解引用运算符箭头是什么的指针指着。” Bruce Eckel: Thinking CPP Vol-one : operator->

提供额外的功能是为了方便,所以你不必调用

a->->func();

你可以这样做:

a->func();

这使得运算符 -> 不同于其他运算符重载。

【讨论】:

  • 这个答案值得更多信任,您可以从该链接下载 Eckel 的书,信息在第一卷的第 12 章。
【解决方案3】:

您不能重载成员访问 .(即 -&gt; 所做的第二部分)。但是,您可以重载一元 dereferencing 运算符 *(即 -&gt; 所做的第一部分)。

C++ -&gt; 运算符基本上是两个步骤的联合,如果您认为x-&gt;y 等同于(*x).y,这一点就很清楚了。当x 是您的类的一个实例时,C++ 允许您自定义如何处理(*x) 部分。

-&gt; 重载的语义有点奇怪,因为 C++ 允许您返回一个常规指针(它将用于查找指向的对象)或返回另一个类的实例,如果这个类也提供了一个 @ 987654331@运营商。在第二种情况下,从这个新实例继续搜索取消引用的对象。

【讨论】:

  • 很好的解释!我猜这对-&gt;* 的含义相同,因为它相当于(*x).* 的形式?
【解决方案4】:

-&gt; 运算符不知道所指向的成员是什么,它只是提供一个对象来执行实际的成员访问。

此外,我认为您没有理由不能提供 const 和非 const 版本。

【讨论】:

    【解决方案5】:

    当你重载 operator->() 时(这里没有传递参数),编译器实际上做的是递归调用 -> 直到它返回一个指向一个类型的实际指针。然后它使用正确的成员/方法。

    这很有用,例如,创建一个封装实际指针的智能指针类。重载的 operator-> 被调用,执行它所做的任何事情(例如,为线程安全而锁定),返回内部指针,然后编译器调用 -> 来获取这个内部指针。

    至于 constness - 它已在 cmets 和其他答案中得到解答(您可以而且应该同时提供两者)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多