После перемещения std::unique_ptr в другой объект можно ли безопасно обращаться к исходному указателю?
После перемещения исходный std::unique_ptr гарантированно становится пустым: он больше не владеет ресурсом, поэтому разыменовывать его нельзя. Сам объект указателя остаётся корректным и пригодным для проверки, уничтожения, присваивания нового значения или вызова reset().
Владение ресурсом переходит к целевому std::unique_ptr; удаление ресурса выполнит именно он, если не будет перемещён дальше или сброшен.
До появления семантики перемещения передача уникально владеющего ресурса часто требовала ручных соглашений: передавать сырой указатель, отдельно описывать смену владельца и следить, чтобы ресурс не удалили дважды.
C++11 добавил перемещающие операции, позволяющие передавать ресурс без копирования самого ресурса. Это стало основой для безопасной передачи владения через std::unique_ptr.
Уникальный указатель нельзя копировать, поскольку копия создала бы двух владельцев одного ресурса. Перемещение решает задачу иначе: исходный владелец отдаёт ресурс и остаётся пустым.
Ошибка возникает, когда после перемещения код продолжает считать исходный указатель владельцем или разыменовывает его. Такое обращение является неопределённым поведением, если указатель пуст.
Перемещающая операция переносит хранимый адрес и, при необходимости, состояние удалителя в целевой объект. После этого исходный std::unique_ptr находится в пустом состоянии; практически это означает, что get() возвращает nullptr, а проверка в условии даёт false.
После std::move(source) сам по себе ничего не перемещает: std::move только превращает выражение в ссылку, допускающую вызов перемещающей операции. Состояние меняет уже конструктор или оператор перемещения std::unique_ptr.
Исходный указатель можно безопасно уничтожить, переназначить или снова наполнить новым ресурсом. Нельзя безопасно разыменовывать его без предварительной проверки и нельзя использовать его как указатель на прежний объект.
Перемещение не продлевает жизнь объекта отдельно от владельца. Объект продолжает существовать, пока им владеет целевой std::unique_ptr или другой последующий владелец, которому ресурс будет передан.
Фабрика создаёт дескриптор операционной системы и передаёт его компоненту, ответственному за закрытие. Рассматривались три варианта: передать сырой указатель с неформальным правилом владения, использовать std::shared_ptr или передать std::unique_ptr перемещением.
Сырой указатель оставляет неясным владельца и допускает двойное освобождение. std::shared_ptr формирует совместное владение, хотя оно не требуется, добавляет управляющий блок и усложняет модель ответственности.
Выбран std::unique_ptr с пользовательским удалителем. После передачи исходная переменная проверяется только как пустой маркер, а принимающий компонент явно становится единственным владельцем и гарантированно освобождает дескриптор при уничтожении.
1. Можно ли вызвать reset() или присвоить новое значение перемещённому std::unique_ptr?
Да. Перемещённый объект остаётся валидным, но пустым. reset() в таком состоянии безопасен, а присваивание нового std::unique_ptr снова делает его владельцем соответствующего ресурса.
Важно отличать валидность объекта указателя от наличия ресурса: валидный пустой std::unique_ptr можно использовать в разрешённых операциях, но нельзя разыменовывать.
2. Что произойдёт, если целевой std::unique_ptr уже владеет ресурсом?
При перемещающем присваивании его прежний ресурс сначала освобождается с использованием его удалителя, затем целевой указатель принимает ресурс из исходного. Поэтому такое присваивание может уничтожить прежний объект целевого владельца.
Это отличается от перемещающего конструктора, где целевой объект ещё не содержит ресурса. При проектировании кода нужно учитывать побочный эффект немедленного освобождения прежнего ресурса.
3. Почему нельзя восстановить прежний объект через исходный указатель после перемещения?
Исходный std::unique_ptr больше не хранит адрес ресурса и не имеет права им владеть. Целевой указатель теперь отвечает за его время жизни, поэтому попытка вручную получить или удалить ресурс через старую переменную нарушила бы модель владения.
Если требуется наблюдать за объектом без передачи владения, адрес следует получить до перемещения и использовать только пока целевой владелец гарантированно сохраняет объект. Такой сырой указатель остаётся невладеющим и не должен переживать объект или удалять его.