我理解大多数运算符重载,除了会员接入操作符 ->、 .*、 ->*等。
->
.*
->*
特别是,传递给这些操作符函数的是什么,应该返回什么?
操作符函数(例如 operator->(...))如何知道引用的是哪个成员?它能知道吗?它有必要知道吗?
operator->(...)
最后,是否有需要考虑的常数因素?例如,当重载类似于 operator[]的内容时,通常需要常量和非常量版本。成员访问操作符是否需要常量和非常量版本?
operator[]
->操作符不知道指向哪个成员,它只是提供一个对象来执行实际的成员访问。
此外,我认为没有理由不提供常量和非常量版本。
当重载运算符-> ()时(这里没有传递任何参数) ,编译器实际上是递归地调用-> ,直到它返回一个指向类型的实际指针。然后使用正确的成员/方法。
例如,这对于创建封装实际指针的智能指针类很有用。重载操作符-> 被调用,执行任何操作(例如,为线程安全而锁定) ,返回内部指针,然后编译器调用-> 来获取这个内部指针。
至于常量——它已经在评论和其他答案中得到了回答(你可以,也应该提供两者)。
操作员-> 是特殊的。
“它还有一些额外的非典型约束: 它必须返回一个对象(或引用一个对象) ,这个对象也有一个指针解引用运算符,或者它必须返回一个指针,这个指针可以用来选择指针解引用运算符箭头指向的是什么。” Bruce Eckel: Thinking CPP 第一卷: Operator->
为了方便起见,提供了额外的功能,所以您不必调用
a->->func();
你可以简单地做:
a->func();
这使得运算符-> 不同于其他运算符重载。
不能重载成员访问 .(即 ->所做的第二部分)。然而,您可以重载一元 解除引用操作符 *(即 ->所做的第一部分)。
.
*
C + + ->操作符基本上是两个步骤的并集,如果您认为 x->y等价于 (*x).y,那么这一点就很明显了。C + + 允许您自定义在 x是类的实例时如何处理 (*x)部分。
x->y
(*x).y
x
(*x)
->重载的语义有些奇怪,因为 C + + 允许返回一个常规指针(用于查找指向的对象) ,或者返回另一个类的实例(如果这个类也提供 ->操作符的话)。在第二种情况下,从这个新实例继续搜索解引用对象。
这是唯一非常棘手的一个。它必须是一个非静态成员函数,并且不接受任何参数。返回值用于执行成员查找。
如果返回值是另一个类类型的对象,而不是指针,那么后续的成员查找也由 operator->函数处理。这就是所谓的“钻取行为”该语言将 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" }
这个只是有点棘手,因为它没有什么特别之处。没有超载版本要求在左侧有一个指向类类型的指针对象,在右侧有一个指向成员类型的指针对象。但是当你超载的时候,你可以接受任何你想要的参数并返回任何你想要的。甚至不需要是非静态成员。
换句话说,这只是一个普通的二进制运算符,如 +、 -和 /
+
-
/
这些不能超载。当左边是类类型时,已经有一个内置的含义。也许将它们定义为左手边的指针会有点意义,但语言设计委员会认为这样做更容易混淆,而不是有用。
重载 ->、 ->*、 .和 .*只能填充表达式未定义的情况,它永远不能改变在没有重载的情况下有效的表达式的含义。