Я играл с конструкторами C ++. Вот мой код:
#include <iostream>
using namespace std;
class ArrayWrapper
{
public:
// default constructor produces a moderately sized array
ArrayWrapper ()
: _p_vals( new int[ 64 ] )
, _size( 64 )
{
cout << "Default constructor: " << this << endl;
}
explicit ArrayWrapper (int n)
: _p_vals( new int[ n ] )
, _size( n )
{
cout << "Constructor: " << this << endl;
}
// move constructor
ArrayWrapper (ArrayWrapper&& other)
: _p_vals( other._p_vals )
, _size( other._size )
{
cout << "Move constructor: " << this << endl;
cout << "Move from: " << &other << endl;
other._p_vals = NULL;
other._size = 0;
}
// copy constructor
ArrayWrapper (const ArrayWrapper& other)
: _p_vals( new int[ other._size ] )
, _size( other._size )
{
cout << "Copy constructor: " << this << endl;
for ( int i = 0; i < _size; ++i )
{
_p_vals[ i ] = other._p_vals[ i ];
}
}
~ArrayWrapper ()
{
cout << "Destructor: " << this << endl;
delete [] _p_vals;
}
public:
int *_p_vals;
int _size;
};
ArrayWrapper foo() {
ArrayWrapper a(7);
cout << "Temp object created!" << endl;
return a;
}int main() {
ArrayWrapper b(foo());
cout << "Finish!" << endl;
}
Выход:
Constructor: 0x7fff5d97bb60
Temp object created!
Destructor: 0x7fff5d97bb60
Move constructor: 0x7fff5d97bbd0
Move from: 0x7fff5d97bbc0
Destructor: 0x7fff5d97bbc0
Finish!
Destructor: 0x7fff5d97bbd0
Первые три строки указывают, что локальная переменная в функции foo () создается с помощью конструктора и уничтожается при возврате функции foo (). 4-я строка указывает, что b построен с использованием конструктора перемещения. Но следующие две строки являются наиболее запутанными: у меня теперь есть новый адрес, который отличается от локальной переменной «a» в foo (), которую я использовал для вызова конструктора перемещения. Когда конструктор копирования заканчивается, ссылка на rvalue исчезает, и вызывается деструктор. Но почему нет конструктора копирования для 0x7fff5d97bbc0? Другими словами, откуда берется 0x7fff5d97bbc0 и как он построен? Просто запрограммировано, что есть еще один вызываемый деструктор, чем вызываемый конструктор.
У меня есть ощущение, что это связано с копией elision. Таким образом, я изменил строку возврата в foo () следующим образом:
return std::move(a);
И вывод:
Constructor: 0x7fff55a7ab58
Temp object created!
Copy constructor: 0x7fff55a7abc0
Destructor: 0x7fff55a7ab58
Move constructor: 0x7fff55a7abd0
Move from: 0x7fff55a7abc0
Destructor: 0x7fff55a7abc0
Finish!
Destructor: 0x7fff55a7abd0
Теперь это, наконец, имело некоторый смысл: в третьей строке показано, что конструктор копирования вызывается до уничтожения «а». Это означает, что при возврате по значению он фактически копировал значение в возвращаемое значение перед уничтожением временной переменной.
Но я все еще запутался в исходной программе (без std :: move ()), потому что, если это действительно вызвано копией elision, не должен ли адрес возвращаемого значения foo () совпадать с локальной переменной «a» ? Теперь, когда он отличается, что означает, что он находится в совершенно отличной позиции в памяти от «а», тогда почему он не вызвал конструктор копирования?
Надеюсь, мой вопрос ясен и понятен.
Изменить: компилятор, который я использовал, был clang ++ с флагом -fno-elide-constructors.
Какой у тебя компилятор, лязг без std::move
:
Constructor: 0x7fff0b8e3b80
Temp object created!
Finish!
Destructor: 0x7fff0b8e3b80
с std::move
:
Constructor: 0x7fffca87eef0
Temp object created!
Move constructor: 0x7fffca87ef30
Move from: 0x7fffca87eef0
Destructor: 0x7fffca87eef0
Finish!
Destructor: 0x7fffca87ef30
Эти два результата гораздо более логичны, чем ваш, так что опять же, каков ваш компилятор?
Редактировать: Это похоже на ошибку с флагом -fno-elide-constructors.
добавление члена int после двух исходных членов приводит к тому же результату, но если int является первым, повреждение памяти! И версия без коррупции заканчивается значением nullptr в главном ArrayWrapper. Посмотрите журнал «и удалите», чтобы поймать ошибочное поведение.
http://coliru.stacked-crooked.com/a/f388c504b442b71d <- после, хорошо
http://coliru.stacked-crooked.com/a/9beced1d5a2aa6e4 <- до, коррупционная свалка
Похоже, вы получаете много копий, с g ++ 4.8 я получаю следующий вывод:
Constructor: 0x7fff88925df0
Temp object created!
Finish!
Destructor: 0x7fff88925df0
если я добавлю: -fno-elide-constructors, то получу:
Constructor: 0x7fff1bd329b0 for this line: ArrayWrapper a(7);
Temp object created!
Move constructor: 0x7fff1bd329f0 for temporary
Move from: 0x7fff1bd329b0 moving from this : ArrayWrapper a(7)
Destructor: 0x7fff1bd329b0 foo is ending so destroy: ArrayWrapper a(7);
Move constructor: 0x7fff1bd329e0 for ArrayWrapper b - it is being created
Move from: 0x7fff1bd329f0 moving from temporary
Destructor: 0x7fff1bd329f0 destroying temporary
Finish!
Destructor: 0x7fff1bd329e0 destroy object b