У меня есть некоторые недоразумения по поводу конструктора перемещения С++. Если компилятор неявно синтезирует конструктор перемещения, что будет делать этот конструктор перемещения? Будет ли просто "this" указывать на объект, который используется для инициализации? есть пример:
struct Foo {
int i;
int *ptr;
};
Foo x;
Foo y(std::move(x));
будет ли неявно синтезированный конструктор перемещения просто заставлять this
в y
указывать на память, в которой находится x
? Если да, то как обеспечить, чтобы x
можно было разрушить после перемещения (если x
уничтожается, будут ли члены в y
действительными)? если нет, то как работает конструктор перемещения y
?
will the implicitly synthesized move constructor just make this in y point to the memory that x is in?
синтезатор движения будет членское перемещение членами данных своих аргументов (x
здесь) для создаваемого объекта (y
здесь).
Также обратите внимание, что для встроенных типов, таких как int
, перемещение аналогично копированию.
how to ensure that x is destructible after the move
Поскольку перемещение — это то же самое, что и копирование для встроенных типов, x
и y
не зависят друг от друга. Поэтому, когда x
будет уничтожен, y
не пострадает.
will the synthesized move constructor set the
ptr
ofx
tonullptr
?
Нет, движок синтеза не сделает этого за вас. Он просто будет перемещать элементы данных по элементам. Для встроенных типов это означает то же самое, что и копирование.
if it does not do this, then the
ptr
ofx
will still point to the memory to which theptr
ofy
points, then you cannot safely destroyx
.
В этом случае вам нужно будет написать определяемый пользователем движущийся элемент, который явно устанавливает ptr
из x
в nullptr
, чтобы при уничтожении x
не затрагивалось y
.
как конструктор перемещения перемещает элементы типа int или string? Будет ли он просто копировать значения этих членов в инициализированный объект класса?
И установит ли синтезированный конструктор перемещения ptr
из x
значение nullptr
? если он этого не сделает, то ptr
из x
все равно будет указывать на память, на которую указывает ptr
из y
, тогда вы не сможете безопасно уничтожить x
.
@kaiyuwei Для встроенных типов, таких как int
, move
это то же самое, что копирование. Больше можно найти здесь: Имеют ли встроенные типы семантику перемещения?. То есть для встроенных типов move
является копией.
@kaiyuwei Нет, синтезатор движения не установит ptr
из x
на nullptr
. Он просто перемещает элементы данных из x
в y
, не делая ничего другого. Также обратите внимание, что для элементов данных встроенного типа, таких как int
, перемещение аналогично копированию. Итак, x
и y
не зависят друг от друга. Если оба указателя x
и y
указывают на одну и ту же память, вам нужно будет написать собственный конструктор перемещения, который установит x
в nullptr. Синтезированный ctor не сделает этого за вас.
@kaiyuwei Вам придется написать определяемый пользователем механизм перемещения, который явно устанавливает ptr
из x
на nullptr
, чтобы при уничтожении x
y
не затрагивалось.
will the implicitly synthesized move constructor just make this in y point to the memory that x is in?
Как заметил Anoop, он будет вызывать конструктор перемещения для каждого члена источника, который нужно переместить (x
), на элементы назначения (y
или this
, если вы представляете себя «внутри» этого синтезированного конструктора перемещения).
Итак, в вашем примере:
int
x.i
будет перемещен на y.i
(this->i
)int*
x.ptr
будет перемещен на y.ptr
(this->ptr
).Обратите внимание, что эти конкретные перемещения будут выполняться путем копирования.
how to ensure that x is destructible after the move (if x is destroyed, will members in y be valid)?
Это всегда разрушаемо. Что касается того, является ли он «действительным», это зависит от того. Конструкция перемещения по умолчанию фактически была копией. Теперь два объекта указывают на то, на что указывает x.ptr
. Если этот int
был ресурсом, «принадлежавшим» x
(возможно, ресурс был строкой целых чисел в конце 0 в куче), то вы только что «поделились» этим ресурсом.
Если вы не хотите «делиться» им, вы могли бы явно написать конструктор перемещения для Foo
, который сбрасывает указатель «перемещенного из» объекта или каким-то образом помечает его как недействительный.
struct Foo {
...
Foo(Foo&& other) {
this->i = other.i;
this->ptr = other.ptr;
other.ptr = nullptr;
}
}
В общем, лучше вообще не полагаться на "перемещенные" объекты, т.е. позаботиться о том, чтобы они были уничтожены как можно скорее.
Спасибо, Жоао, поскольку x.ptr
и y.ptr
указывают на один и тот же объект, как вы можете говорить, что x
разрушаем? поскольку, если вы уничтожите x
, то объект, на который указывает y.ptr
, также будет уничтожен, что сделает ```y.ptr''' недействительным.
Правильно, но в примере, который вы впервые опубликовали, такого деструктора члена ptr
не было! Таким образом, то, на что указывает ptr
, будет уничтожено нет. Если есть мы такой деструктор, то этот деструктор, вероятно, должен проверять, является ли ptr
nullptr
, а не уничтожать его. И это хорошая причина определить конструктор перемещения, как я предложил.
Теперь мне достаточно ясно. Большое спасибо!
1. Нет, и 2. как работают операторы перемещения (по сравнению с тем, как работают копировщики), подробно рассматривается во множестве вопросов на этом сайте. этот например. И особенно этот.