为什么返回*this会导致无限循环?

3
class binaryOperators 
{
    public:
        int i;

        binaryOperators (int tempI = 0)
        {
            i = tempI;
        }

        binaryOperators operator+ (const binaryOperators &right);
};

binaryOperators binaryOperators :: operator+ (const binaryOperators &right)
{
    return binaryOperators (*this + right.i);
}

binaryOperators operator+ (const binaryOperators &left, const binaryOperators &right)
{
    return binaryOperators (left.i + right.i);
}

int main ()
{
    binaryOperators obj (10);

    obj = 11 + obj;

    obj = obj + 11;

    return 0;
}

所以,这个语句obj = 11 + obj;调用了带有明确参数规定的函数。 而这个语句obj = obj + 11;调用了类的成员函数。很好。 问题是第二个调用导致了无限循环。原因是什么,如何避免?

发布 binaryOperators 的声明。 - Luchian Grigore
2个回答

9
问题出在这个语句中:
return binaryOperators (*this + right.i);

*this的类型是binaryOperators,因此需要一个左侧参数为binaryOperators(或引用)的运算符。

可能匹配的运算符有两种,因此右侧参数需要是const binaryOperators &类型。因此,使用适当的构造函数将right.i转换为临时binaryOperators。

结果,成员运算符最终调用自身,然后调用自己,再次调用自己,以此类推,导致无限递归(即您看到的无限循环)。

您可以通过使用以下方法来修复这个问题:

return binaryOperators (this->i + right.i);

你说 *this是一个二元运算符,所以需要一个左侧参数类型(或引用)为binaryOperators的运算符。 你的意思是因为+/-/*不能在对象上工作...好吧,我没有理解那一行..抱歉。 - Aquarius_Girl
@AnishaKaul 我稍微改了一下措辞,也许现在更清楚了。 +-/* 都是运算符,只有在定义了适当的运算符重载时才能作用于类实例(也称为对象)。给定 *this 作为左操作数,唯一匹配的运算符是由您定义的运算符,其中选择成员。然后相应地将右操作数转换为临时二进制运算符。通过使用 this->i,您切换到不同的 operator+ 重载,即由编译器提供的接受两个 int 的重载。 - zennehoy
感谢您的努力。那么,您的意思是如果我写(obj + 5),编译器会搜索一个具有两个参数的函数 - 一个类对象和一个整数? - Aquarius_Girl
@AnishaKaul 是的,没错。如果它找不到一个接受 int 的函数,它会尝试将 int(或对象)转换为可以使用的类型。在给定的情况下,int 被转换为最匹配的可用参数类型。 - zennehoy
好的,所以int类型被转换成类的对象,现在情况是左参数为this,右参数为“转换为该类对象的int”?那么递归从哪里开始? - Aquarius_Girl
@AnishaKaul 在操作符由于是最佳匹配而调用自身的点上。请参考thiton的修订答案,我认为那样很清楚了。 - zennehoy

5

binaryOperators::i(类型为int)向binaryOperators的转换是隐式的(即未声明为explicit)。

return binaryOperators (*this + right.i); // (1)
binaryOperators binaryOperators :: operator+ (const binaryOperators &right); // (2)
binaryOperators operator+ (const binaryOperators &left, const binaryOperators &right); // (3)

在第一行(1)中,有两个operator+函数可供考虑:成员版本(2)和自由版本(3)。由于LHS是类型为binaryOperators&,因此首选使用成员版本,并且该版本适用。它的参数类型为const binaryOperators &,而在第一行给出的参数类型为int,因此编译器尝试将int转换为const binaryOperators &

由于存在一个具有一个参数的非explicit构造函数,因此它被用作从intconst binaryOperators &的隐式转换。现在我们有两个操作数,类型分别为binaryOperators&const binaryOperators &,可以调用(2)中的operator+,回到了起点。

教训:不要过度进行隐式转换。


@AnishaKaul:是的,正如我所想的那样。由于binaryOperatorsint(一个参数构造函数)进行了转换构造函数,因此会发生隐式转换。在构造函数前面写上explicit,问题就会消失。养成所有单参数构造函数都这样做的习惯,可以节省一些烦恼。 - thiton
@thiton 实际上问题并不会完全消失,但至少编译器会告诉你错误出现的位置(即找不到匹配的运算符)。 - zennehoy
thiton 隐式转换如何导致递归?请详细解释一下。 - Aquarius_Girl
现在我们有两个类型为binaryOperators&和const binaryOperators&的操作数,可以调用(2)中的operator+,我们回到了起点。那么递归是如何开始的呢? - Aquarius_Girl
1
第一行是从 operator+ 版本 (2) 中调用的。因此,当调用 (2) 时,会运行 (1),它又调用 (2),然后运行 (1) 等等。 - thiton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接