Почему деструктор класса C ++ вызывается после назначения с помощью конструктора преобразования? - PullRequest
0 голосов
/ 09 июля 2020

Учитывая этот класс:

class Baz
{
public:
    Baz() : Baz(0) {}
    Baz(int i) { _i = i; }
    Baz(Baz const &b) { _i = b._i * 10; }
    ~Baz() { }
private:
    int _i;
};

Если я отлаживаю и прохожу через этот код:

Baz a = 4;

Конструктор Baz, который принимает int, вызывается, как и ожидалось , и я получаю Baz с _i из 4. Это хорошо.

Но если я сделаю это:

Baz b;
b = 4;

Первая строка вызывает конструктор по умолчанию, как и ожидалось. Вторая строка вызывает int конструктор , а затем деструктор .

Мое первое ожидание заключалось в том, что вторая строка второго примера просто вызовет конструктор int при назначении b. Я не ожидал, что деструктор будет вызван, но если он сначала преобразует целое число 4 в Baz перед присваиванием, имеет смысл уничтожить его после. Но тогда я ожидал бы, что конструктор копирования будет вызван при присвоении временного значения b. Уничтоженный Baz имеет значение 4 вместо _i, так что это не объект, созданный в первой строке.

Что именно здесь происходит? Почему разница между этими двумя сценариями ios?

Ответы [ 4 ]

3 голосов
/ 09 июля 2020

Эта строка:

Baz a = 4;

Эквивалентна:

Baz a(4);   // It simply constructs the object.
            // Note the `=` is not an assignment when
            // used in a declaration like this. It is
            // simply short hand (syntactic sugar) for
            // a single argument constructor.

В то время как эти две строки:

Baz b;      // Default construct thus setting _i to zero.
b = 4;      // This **IS** an assignment and needs an assignment
            // operator (or there is a compiler error).

            // Note there is no assignment operator that takes
            //      an integer. But there is a default assignment
            //      operator that takes a Baz by const reference.
            //
            //      This is auto generated by the compiler.
            //      See rule of 5
            //         Baz& operator=(Baz const& copy): _i(copy._i){return *this)
            //
            // So the compiler must convert the integer to Baz before 
            // an assignment is allowed. The compiler is allowed to 
            // create an Object using a one parameter constructor so
            // that line is equivalent too:

  // Equiv
  b = Baz(4);  // So this creates a temporary Baz object here.
               // Then the assignment operator is called.
               // Then at the end of the statement the temporary
               // object must be destroyed so we call the destructor  
               // on the temporary.
3 голосов
/ 09 июля 2020

Эта строка:

b = 4;

сначала вызывает int конструктор Baz, чтобы создать временный Baz со значением 4. Этот временный объект уничтожается в точке с запятой.

Присваивание не вызывает конструктор копирования, а фактически вызывает оператор присваивания копии, имеющий подпись Baz& operator=(Baz const &);.

2 голосов
/ 09 июля 2020

Оператор Baz a = 4; - это просто синтаксический сахар для Baz a(4);, поэтому он напрямую вызывает конструктор преобразования Baz(int).

Baz не имеет определенного operator=(int), но имеет вместо этого operator=(const Baz&) (который создается компилятором в вашем примере). И Baz имеет конструктор Baz(int). А параметр const Baz& может связываться с rvalue, и существует только одно неявное преобразование из int в Baz. Таким образом, компилятор может обрабатывать оператор b = 4; как b = Baz(4);, ie, он создает временный объект Baz и передает его в operator=. Временное уничтожается в конце оператора, который его создает (ie, в ;).

Итак, это конструктор int и деструктор этого временного Baz объекта, который вы видите.

1 голос
/ 09 июля 2020

Конструктор может использоваться только для создания нового объекта.

b = 4; не создает b, поэтому он не может вызвать конструктор для b. Вместо этого он создает временный экземпляр Baz с помощью конструктора Baz(int i), а затем назначает временный b (используя operator=, который генерируется компилятором в вашем случае).

Временный уничтожается в конце полного выражения (т.е. когда элемент управления достигает ;), поэтому вы видите вызов деструктора.

...