Что происходит, когда lvalue назначается для ссылки на rvalue? Нет разрушения временного объекта?

#include <iostream>
using namespace std;
#include <cstring>

class Word{
private:
char* ptr = nullptr;
public:
Word(){
cout << "default constructor" << endl;
}
Word(const char* sentence):ptr{new char [strlen(sentence)+1]}{
strcpy(ptr, sentence);
cout << "conversion constructor: " << ptr << endl;
}
Word(const Word& w):ptr{new char [strlen(w.ptr)+1]}{
strcpy(ptr, w.ptr);
cout << "copy constructor: "<< ptr << endl;
}
~Word(){
cout << "destructor: " << ptr << endl;
}
};

int main(){
Word a ("A stands for apple!");
Word&& b = "B stands for Banana, rvalue ref";
b = a;
}

Мой результат Затмения:

conversion constructor: A stands for apple!
conversion constructor: B stands for Banana, rvalue ref
destructor: A stands for apple!
destructor: A stands for apple!

Мое возбуждение:

conversion constructor: A stands for apple!
conversion constructor: B stands for Banana, rvalue ref
destructor: B stands for Banana, rvalue ref
destructor: A stands for apple!
destructor: A stands for apple!

Я смущен этим шагом.

b = a;

Когда a присваивается b, он может предположить сначала уничтожить временный объект (с помощью cstring «B обозначает банан, rvalue ref»), который содержит b, а затем присвоить значение a b. Почему в результате Eclipse он не выполняет уничтожение временного объекта?

1

Решение

Ваше ожидание неверно. Разрушений не может быть больше, чем конструкций.

Когда a присваивается b, это может предполагать сначала уничтожить временный объект

Нет. b относится к временному объекту. Присвоение объекту не приводит к его уничтожению.

Что происходит: неявно сгенерированный оператор присваивания Word назначит всех участников. Итак, после присвоения предыдущее значение b.ptr утечка и имеет то же значение (указывает на ту же строку), что и a.ptr,

4

Другие решения

b = a; вызывает оператор присваивания, а не конструктор копирования. Если явно удалить, код не будет компилироваться:

// trimmed...
Word(const Word& w):ptr{new char [strlen(w.ptr)+1]}{
strcpy(ptr, w.ptr);
cout << "copy constructor: "<< ptr << endl;
}
Word& operator = (const Word& w) = delete;

Строка компиляции:

$ g++ rvalue-ref.cpp -o rvalue-ref
rvalue-ref.cpp: In function ‘int main()’:
rvalue-ref.cpp:45:9: error: use of deleted function ‘Word& Word::operator=(const Word&)’
b = a;
^
rvalue-ref.cpp:20:15: note: declared here
Word& operator = (const Word& w) = delete;
^~~~~~~~

Компилятор предоставит оператор присваивания по умолчанию, поэтому код в вашем вопросе использует это. Чтобы увидеть, что происходит, добавьте операторы копирования-назначения и перемещения-назначения.

Word& operator = (const Word& w) {
auto temp = new char [strlen(w.ptr)+1];
strcpy(temp, w.ptr);
delete [] ptr;
ptr = temp;
cout << "assignment operator: " << ptr << endl;
return *this;
}
Word& operator = (Word&& w) {
std::swap(ptr, w.ptr);
cout << "swap operator: " << ptr << endl;
return *this;
}

Имея это, я получаю ожидаемый результат:

conversion constructor: A stands for apple!
conversion constructor: B stands for Banana, rvalue ref
assignment operator: A stands for apple!
destructor: A stands for apple!
destructor: A stands for apple!

Кроме того, вы теряете память. Ваш деструктор должен выглядеть так:

~Word(){
cout << "destructor: " << ptr << endl;
delete [] ptr;
}

$ valgrind ./rvalue-ref

==10736== Memcheck, a memory error detector
==10736== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==10736== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==10736== Command: ./rvalue-ref
==10736==
conversion constructor: A stands for apple!
conversion constructor: B stands for Banana, rvalue ref
assignment operator: A stands for apple!
destructor: A stands for apple!
destructor: A stands for apple!
==10736==
==10736== HEAP SUMMARY:
==10736==     in use at exit: 0 bytes in 0 blocks
==10736==   total heap usage: 5 allocs, 5 frees, 73,800 bytes allocated
==10736==
==10736== All heap blocks were freed -- no leaks are possible
==10736==
==10736== For counts of detected and suppressed errors, rerun with: -v
==10736== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

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

Word& operator = (const Word& w) {
Word temp(w);
std::swap(ptr, temp.ptr);
cout << "assignment operator: " << ptr << endl;
return *this;
}
0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector