【问题标题】:C++ operator overloadingC++ 运算符重载
【发布时间】:2013-06-07 14:39:00
【问题描述】:

为什么下面的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;
}

【问题讨论】:

  • 仅供参考;这是一个创建 ACCA 的结构,作为给读者的练习。

标签: c++ class operator-overloading


【解决方案1】:

operator int() 被调用了两次,因为您没有重载operator+。编译器不知道如何将Base 添加到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;
}

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 由于operator int() 没有标记为const,签名直接告诉编译器它应该期望调用修改对象的状态。然而,即使对于 const 运算符,也不允许完全优化第二个调用,因为其中可能有类似 cout 的东西(这里确实是这样)。
    • 并且我们可以把一个有副作用的函数(比如写入标准输出)想象成每次调用它时都会返回一个新版本的宇宙,这是另一种解释“返回每次调用时都有不同的值”。
    【解决方案3】:

    每个操作数调用一次,是否是同一个实例无关紧要。

     obj = obj+obj;
    

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

         Base obj;
        

        当您指定添加obj+obj 时,编译器需要找出在obj 上使用+ 的方法。由于您没有为 Base 覆盖 operator+,因此等式的每一边都调用到 int() 的转换:

        obj+obj
        

        这打印"CC"

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

        obj = obj+obj; // Assignment prints "A"
        

        【讨论】:

          【解决方案6】:

          首先,这一行:

          Base obj;
          

          默认构造对象obj,方法是选择接受整数的构造函数,默认值1。这负责将第一个 A 打印到标准输出。

          那么,这个表达式:

          obj + obj
          

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

          然后,分配给obj in:

          obj = obj + obj
          

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

          Base& operator = (Base const&);
          

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

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

          【讨论】:

            【解决方案7】:
            obj = obj+obj;
                  ^^^--------obj converted to int here
                      ^^^----obj converted to int here
            ^^^^^------------Base(int) ctor and default operator= called here
            

            重载强制转换运算符通常不是一个好主意,除非您了解成本并知道在您的特定情况下的好处大于它们的事实。

            【讨论】:

              【解决方案8】:

              第一个A来自

              Base obj;

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

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

              【讨论】:

                【解决方案9】:

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

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

                Base obj;
                

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

                下一行有点复杂:

                obj = obj+obj;
                

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

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

                最后,赋值表达式obj.operator=( temp )被执行。这里的关键字是temp。为了使default operator= 工作,因为它没有过载,你必须在右边有一个Base 对象。实际上有可能拥有它,因为Base 使用int 来构建新实例。好的,所以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;
                

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

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2016-02-19
                  • 1970-01-01
                  • 2016-04-08
                  • 2012-06-02
                  • 2014-01-14
                  • 2013-03-23
                  • 2013-12-03
                  相关资源
                  最近更新 更多