Почему конструктор преобразования вызывается в отсутствие оператора присваивания? - PullRequest
0 голосов
/ 06 января 2019

Сегодня мой вопрос: почему конструктор вызывается в отсутствие предоставленного пользователем оператора присваивания? Я знаю разницу между конструктором и оператором присваивания по отношению к символу =. Если объект не существовал ранее до этой строки, конструктор неявно вызывается.

std::string name = "Bjarne"; // implicit constructor

Если объект уже существовал ранее до этой строки, вызывается оператор присваивания.

std::string name;
name = "Bjarne";   // assignment operator

Теперь у меня есть class Number здесь, который является теговым объединением. Это просто число, означающее либо int, либо float. У меня есть несколько конструкторов преобразования, которые позволяют пользователю неявно присваивать значения Number. Я ожидаю, что они будут вызваны в такой ситуации:

Number num1 = 8;

Чтобы точно знать, когда и когда вызывается конструктор, я помещаю операторы print в тела конструкторов.

#include <cstdlib>
#include <iostream>

using std::cin;
using std::cout;
using std::cerr;
using std::endl;
using std::ostream;


/* Unions can be anonymous (unnamed).
 * This form is used when nesting a union inside a struct or class that contains an extra data member
 * to indicate what the union contains.
 * This arrangement is called a tagged union (or managed union).
 * It can be used as a "polymorphic" class which can be several things at once, for example to create an array
 * of different data types.
 */

class Number {
    friend ostream& operator<<(ostream& os, Number& num);
  public:
    Number(int i = 0) : DATA_TYPE(INTEGER), i(i) { cerr << "Number(" << i << ")\n"; }

    Number(unsigned int u) : DATA_TYPE(INTEGER), i(static_cast<int>(u)) { cerr << "Number((signed) " << i << ")\n"; }

    Number(float f) : DATA_TYPE(FLOAT), f(f) { cerr << "Number(" << f << ")\n"; }

    Number(double d) : DATA_TYPE(FLOAT), f(static_cast<float>(d)) { cerr << "Number((float) " << f << ")\n"; }    

    Number(const Number& rhs) : DATA_TYPE(rhs.DATA_TYPE), i(rhs.i) { cerr << "Number((Number) " << *this << ")\n"; }
  private:
        enum data_type { INTEGER, FLOAT } DATA_TYPE;

    union {
        int i;
        float f;
    };
};


ostream& operator<<(ostream& os, Number& num)
{
    switch (num.DATA_TYPE) {
        case Number::INTEGER:
      cout << num.i;
          break;
        case Number::FLOAT:
      cout << num.f;
          break;
    default:
      cout << 0;
      break;
    }

    return os;
}


int main()
{
    Number num1 = 5;  // conversion constructor
    cout << num1 << endl;
    num1 = 12.5;
    cout << num1 << endl;

    return EXIT_SUCCESS;
}

Когда я запускаю этот код, он выдает неожиданный вывод для меня:

Number(5)
5
Number((float) 12.5)
12.5

Я вижу, что при инициализации Number num1 вызывается конструктор, принимающий int в качестве параметра. Затем я печатаю значение num1. Затем на следующей строке кажется, что вызывается конструктор, принимающий double в качестве параметра. Это странно. До этой строки num1 уже существовал как объект. Так что вместо этого он должен логически вызывать оператор присваивания. Вместо этого я не определил оператор присваивания, поэтому я предполагаю, что будет вызван оператор присваивания, сгенерированный компилятором.

Я хочу знать, что произойдет, если я не предоставлю пользователю оператор присваивания? num1 = something; Вызывается ли оператор присваивания по умолчанию, предоставляемый компилятором, или конструктор? Что происходит, когда конструктор вызывается для объекта, который уже существует? Этот объект "реконструирован"? Так что же на самом деле происходит в этом случае?

Ответы [ 2 ]

0 голосов
/ 06 января 2019

Благодаря Jarod42 я могу понять это. Поэтому, как он предположил, временный Number объект незаметно создается на правой стороне =, затем вызывается оператор присваивания, и в конце этой строки временный объект уничтожается.

Я добавил следующий код в класс Number, чтобы увидеть, что происходит.

Number& operator=(const Number& rhs)
{
    cerr << "operator=(const Number&)" << endl;
return *this;
}

~Number() { cerr << "~Number()" << endl; }

С помощью этой main() функции

int main()
{
    Number num1 = 5;
    num1 = 12.5;

    cout << endl;

    Number num2 = 8;
    num1 = num2;

    return EXIT_SUCCESS;
}

Будет получен следующий вывод:

Number(5)
Number((float) 12.5)
operator=(const Number&)
~Number()

Number(8)
operator=(const Number&)
~Number()
~Number()
0 голосов
/ 06 января 2019

Я хочу знать, что происходит при отсутствии оператора назначения, предоставленного пользователем, когда я делаю это num1 = something;?

num1 = 12.5;

звонки неявные Number::operator =(const Number&).

Так создается временное число из double.

Вызывается ли оператор присваивания по умолчанию, предоставляемый компилятором, или вызывается конструктор?

Оба. Конструктор вызывается для временного, а не существующего объекта.

Что происходит, когда конструктор вызывается для объекта, который уже существует? Этот объект "реконструирован"? Так что же на самом деле происходит в этом случае?

Вы не можете вызвать конструктор для существующего объекта. Самое близкое, что вы можете сделать, это Place-New .

...