Перегрузка оператора С++

Почему приведенная ниже программа на С++ выводит "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;
}

Ответы

Ответ 1

Во-первых, эта строка:

Base obj;

Объект default-constructs obj, выбирая конструктор, который принимает целое число со значением по умолчанию 1. Это отвечает за то, что первый A печатается на стандартный вывод.

Тогда это выражение:

obj + obj

Требуется выбрать жизнеспособную перегрузку operator +. В этом случае, поскольку obj имеет пользовательское преобразование в int, выбирается встроенный operator +, и оба аргумента преобразуются в int. Это отвечает за то, что два C печатаются на стандартный вывод.

Затем назначение obj в:

obj = obj + obj

Нужно вызвать неявно сгенерированный operator = для Base. Неявно сгенерированный operator = имеет подпись:

Base& operator = (Base const&);

Это означает, что выражение в правой части знака равенства, которое имеет тип int, должно быть преобразовано во временный объект Base, из которого назначается obj (ссылочный параметр неявно- сгенерированный operator = связан с этим временным).

Но для создания этого временного элемента из int в свою очередь требуется вызвать конструкцию преобразования Base, которая снова принимает int, которая отвечает за то, что второй A печатается на стандартный вывод.

Ответ 2

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;
}

Ответ 3

Когда вы создаете объект, вы получаете первый "A":

 Base obj;

Когда вы указываете добавить obj+obj, компилятор должен выяснить способ использования + в obj. Поскольку вы не переопределили operator+ для Base, преобразование в int() вызывается для каждой стороны уравнения:

obj+obj

Отпечатает "CC".

Затем вы назначаете obj, который имеет тип Base, поэтому запускается конструктор, который может принимать int (i + i от оператора int()), который печатает "A":

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

Ответ 4

obj = obj+obj;
      ^^^--------obj converted to int here
          ^^^----obj converted to int here
^^^^^------------Base(int) ctor and default operator= called here

Перегрузка операторов переноса не является хорошей идеей, если вы не понимаете затраты и не знаете, что преимущества в вашем конкретном случае перевешивают их.

Ответ 5

как ниже программа С++ вычисляет значение "ACCA"?

Первая указанная буква - "A". Этот вывод связан с этой строкой:

Base obj;

... в котором вы создаете новый экземпляр Base.

Следующая строка немного сложна:

obj = obj+obj;

Обычно это переводится на obj.operator+( obj ), но у вас нет оператора + перегружено в классе Base, поэтому этот перевод неверен. Оставшаяся вероятность заключается в том, что оператор + на самом деле является оператором numeric add.

И да, это возможно, так как вы предоставили приведение к int. Таким образом, можно преобразовать каждый член уравнения в int... и поэтому operator int вызывается дважды. Действительное количество раз operator int вызывается, зависит от активированных оптимизаций. Например, компилятор мог понять, что оба термина совпадают, а затем создать новый временный, как только operator int был вызван в первый раз. В этом случае вы увидите CA вместо CC.

Наконец, выполняется выражение-присваивание obj.operator=( temp ). И здесь ключевое слово temp. Для того, чтобы по умолчанию operator= работать, поскольку он не перегружен, вы должны иметь объект Base справа. Фактически это возможно, поскольку Base использует int для сборки новых экземпляров. Итак, результат obj + obj был int (его называют "x" ), а компилятор создает временный объект класса Base, который построен с номером x, по мере выполнения следующей строки

Base temp( x );

То, как видно последнее письмо, - это "А". Опять же, многие компиляторы могут избежать создания временных рядов в некоторых случаях, поэтому в конце можно увидеть "A".

Обратите внимание, что эта строка:

obj = obj + obj

Таким образом, разлагается на:

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

Последняя команда имеет результат, как если бы память, в которой сидит obj, будет занята содержимым temp (то, что роль конструктора копии по умолчанию, которая выполняет operator= для каждого члена класс, снова см. "правило из трех" ).

Перегрузка оператора включает в себя множество проблем, которые, возможно, не предусмотрены, если вы не знаете более или менее глубокого знания языка, как вы можете видеть. Учтите также, что такие языки, как Java, полностью предотвращают его использование, а С# - с контролируемой точки зрения.

Ответ 6

Вы ссылаетесь на obj дважды в выражении obj+obj, и каждая такая ссылка должна быть преобразована в целое число.

Это не невозможно (хотя это ужасная идея), что такое преобразование может быть "stateful", т.е. оно может по дизайну возвращать другое значение каждый раз, когда оно вызывается. В конце концов, значение, представленное obj, может измениться (это может быть счетчик или что-то в этом роде). Поэтому компилятор должен оценить его заново для каждой ссылки.

Ответ 7

Он вызывает один раз для операнда, неважно, является ли он одним и тем же экземпляром.

 obj = obj+obj;

Он "отличает" первый операнд к int, а затем второй.

Вам нужно перегрузить оператор +, если вы хотите избежать этого неявного приведения.

Ответ 8

Он вызывает operator int() дважды, потому что ему нужно преобразовать оба obj в int, прежде чем он сможет их добавить.

Ответ 9

Первый A происходит от

Base obj;

Два Cs происходят от преобразования obj в obj+obj в int, поскольку вы не перегружали operator +.

Последний A получается из obj = преобразования полученного int в obj.