15

为什么下面的 C++ 程序输出“ACCA”?为什么会operator int()调用两次?

#include "stdafx.h"
#include <iostream>

using namespace std;

class Base {
public:
    Base(int m_var=1):i(m_var){
        cout<<"A";
    }
    Base(Base& Base){
        cout<<"B";
        i=Base.i;
    }
    operator int() {
        cout<<"C";
        return i;
    }
private:
    int i;
};

int main()
{
    Base obj;
    obj = obj+obj;
    return 0;
}
4

9 回答 9

30

首先,这一行:

Base obj;

obj通过选择接受具有默认值的整数的构造函数来默认构造对象1。这负责首先A被打印到标准输出。

然后,这个表达式:

obj + obj

需要选择一个可行的重载operator +. 在这种情况下,由于obj有一个用户定义的转换为int,因此选择了内置operator +参数,并且两个参数都转换为int。这负责将两个Cs 打印到标准输出。

然后,分配到obj

obj = obj + obj

需要调用隐式生成operator =的 for Base。隐式生成的operator =具有签名:

Base& operator = (Base const&);

这意味着等号右侧的表达式,类型为int,必须转换为Base从中obj分配的临时对象(隐式生成的引用参数operator =绑定到此临时对象)。

但是从 an 中创建这个临时需要调用再次接受 anint的转换构造,它负责将第二个打印到标准输出。BaseintA

于 2013-06-11T16:40:51.630 回答
9

operator int()被调用两次,因为你没有重载operator+. 编译器不知道如何将 a 添加Base到 a Base,因此它们被转换为int(因为您教它如何做到这一点),它确实知道如何做到这一点。以下代码打印ADA

#include <iostream>

using namespace std;

class Base {
public:
    Base(int m_var=1):i(m_var){
        cout<<"A";
    }
    Base(Base& Base){
        cout<<"B";
        i=Base.i;
    }
    operator int() {
        cout<<"C";
        return i;
    }
    int operator+(Base& Base)
    {
        cout<<"D";
        return i+Base.i;
    }
private:
    int i;
};

int main()
{
    Base obj;
    obj = obj+obj;
    return 0;
}
于 2013-06-11T16:39:39.587 回答
5

当你构造对象时,你会得到第一个“A”:

 Base obj;

当你指定要添加obj+obj时,编译器需要想办法使用+on obj。由于您没有覆盖operator+for ,因此对等式的每一侧都调用Baseto 的转换:int()

obj+obj

这打印"CC"

然后,您分配给obj,它的类型是Base,因此可以接受 int (i + i来自int()运算符)的构造函数运行,它打印“A”:

obj = obj+obj; // Assignment prints "A"
于 2013-06-11T16:40:45.880 回答
5
obj = obj+obj;
      ^^^--------obj converted to int here
          ^^^----obj converted to int here
^^^^^------------Base(int) ctor and default operator= called here

除非您了解成本并知道您的特定情况下的好处超过它们,否则重载演员操作员通常不是一个好主意。

于 2013-06-11T16:42:13.237 回答
2

下面的 C++ 程序如何计算到“ACCA”?

显示的第一个字母是“A”。此输出与此行有关:

Base obj;

...您正在其中创建 Base 的新实例。

下一行有点复杂:

obj = obj+obj;

通常, this 被翻译成obj.operator+( obj ),但你没有在 class 中重载 operator + Base,所以这个翻译是无效的。剩下的可能性是运算符 + 实际上是数字加法运算符。

是的,这是可能的,因为您已经为int. 因此可以将方程的每一项转换为int... ,因此operator int被调用两次。调用的实际次数operator int取决于激活的优化。例如,编译器可以意识到这两个术语是相同的,然后operator int在第一次调用时创建一个新的临时项。在这种情况下,您会看到 CA 而不是 CC。

最后,执行赋值表达式obj.operator=( temp )。这里的关键字是temp。为了使默认值 operator=起作用,因为它没有重载,所以你必须Base在右边有一个对象。实际上有可能拥有它,因为Base使用 anint来构建新实例。好的,所以结果obj + obj是一个int(比如说它被称为“x”)数字,编译器创建了一个Base用数字x构造的类的临时对象,因为执行了以下行:

Base temp( x );

这就是看到的最后一个字母是“A”的方式。同样,许多编译器可以避免在某些情况下构建临时变量,因此最后可能看不到“A”。

请注意,这一行:

obj = obj + obj

因此分解为:

int x = ( (int) obj ) + ( (int) obj );
Base temp( x );
obj = temp;

最后一条指令的结果是,objsits 所在的内存似乎将被以下内容占用temp(这是默认复制构造函数的作用,它operator=为类的每个成员执行,再次参见“三规则”)。

如您所见,如果您对语言没有或多或少的深入了解,则运算符重载涉及许多可能无法预见的问题。还要考虑到像 Java 这样的语言完全禁止使用它,而 C# 从受控的角度允许它使用。

于 2013-06-11T16:53:26.610 回答
1

您在表达式中引用了 obj 两次,obj+obj并且每个此类引用都必须转换为整数。

这种转换可能是“有状态的”并不是不可能的(尽管这是一个可怕的想法)——也就是说,它可以通过设计在每次调用时返回不同的值。毕竟, 代表的值obj可能会改变(它可能是一个计数器或类似的东西)。所以编译器必须为每个引用重新评估它。

于 2013-06-11T16:40:18.800 回答
1

每个操作数调用一次,如果它是同一个实例都没有关系。

 obj = obj+obj;

它将第一个操作数“转换”为一个 int,然后是第二个。

如果要避免这种隐式强制转换,则需要重载运算符 +。

于 2013-06-11T16:40:19.707 回答
1

它调用operator int()了两次,因为它需要将两个 obj 都转换为 int 才能添加它们。

于 2013-06-11T16:40:40.957 回答
1

第一个 A 来自

Base obj;

这两个 C 来自将 obj 转换obj+obj为,int因为您没有超载operator +.

最后一个 A 来自obj =将结果转换intobj

于 2013-06-11T16:44:38.990 回答