#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 он не выполняет уничтожение временного объекта?
Ваше ожидание неверно. Разрушений не может быть больше, чем конструкций.
Когда a присваивается b, это может предполагать сначала уничтожить временный объект
Нет. b
относится к временному объекту. Присвоение объекту не приводит к его уничтожению.
Что происходит: неявно сгенерированный оператор присваивания Word
назначит всех участников. Итак, после присвоения предыдущее значение b.ptr
утечка и имеет то же значение (указывает на ту же строку), что и a.ptr
,
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;
}