重载成员访问操作符-> ,. *

我理解大多数运算符重载,除了会员接入操作符 ->.*->*等。

特别是,传递给这些操作符函数的是什么,应该返回什么?

操作符函数(例如 operator->(...))如何知道引用的是哪个成员?它能知道吗?它有必要知道吗?

最后,是否有需要考虑的常数因素?例如,当重载类似于 operator[]的内容时,通常需要常量和非常量版本。成员访问操作符是否需要常量和非常量版本?

102511 次浏览

->操作符不知道指向哪个成员,它只是提供一个对象来执行实际的成员访问。

此外,我认为没有理由不提供常量和非常量版本。

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

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

至于常量——它已经在评论和其他答案中得到了回答(你可以,也应该提供两者)。

操作员-> 是特殊的。

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

为了方便起见,提供了额外的功能,所以您不必调用

a->->func();

你可以简单地做:

a->func();

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

不能重载成员访问 .(即 ->所做的第二部分)。然而,您可以重载一元 解除引用操作符 *(即 ->所做的第一部分)。

C + + ->操作符基本上是两个步骤的并集,如果您认为 x->y等价于 (*x).y,那么这一点就很明显了。C + + 允许您自定义在 x是类的实例时如何处理 (*x)部分。

->重载的语义有些奇怪,因为 C + + 允许返回一个常规指针(用于查找指向的对象) ,或者返回另一个类的实例(如果这个类也提供 ->操作符的话)。在第二种情况下,从这个新实例继续搜索解引用对象。

->

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

如果返回值是另一个类类型的对象,而不是指针,那么后续的成员查找也由 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"
}

->*

这个只是有点棘手,因为它没有什么特别之处。没有超载版本要求在左侧有一个指向类类型的指针对象,在右侧有一个指向成员类型的指针对象。但是当你超载的时候,你可以接受任何你想要的参数并返回任何你想要的。甚至不需要是非静态成员。

换句话说,这只是一个普通的二进制运算符,如 +-/

.*.

这些不能超载。当左边是类类型时,已经有一个内置的含义。也许将它们定义为左手边的指针会有点意义,但语言设计委员会认为这样做更容易混淆,而不是有用。

重载 ->->*..*只能填充表达式未定义的情况,它永远不能改变在没有重载的情况下有效的表达式的含义。