Передача unique_ptr в функции
Я пытаюсь "модернизировать" существующий код.
- У меня есть класс, который в настоящее время имеет переменную-член "Device * device_".
- Он использует новое для создания экземпляра в некотором коде инициализации и имеет "delete device_" в деструктории.
- Функции-члены этого класса вызывают множество других функций, которые принимают Device * в качестве параметра.
Это хорошо работает, но чтобы "модернизировать" мой код, я думал, что должен изменить переменную, которая будет определена как "std::unique_ptr<Device> device_"
, и удалить явный вызов для удаления, что делает код более безопасным и вообще лучше.
Мой вопрос -
- Как мне передать переменную device_ всем функциям, которые нуждаются в ней в качестве параметра?
Я могу вызвать .get, чтобы получить необработанный указатель в каждом вызове функции. Но это кажется уродливым и извращает некоторые причины использования unique_ptr в первую очередь.
Или я могу изменить каждую функцию, чтобы вместо того, чтобы принимать параметр типа "Устройство *", теперь он принимает параметр типа "std:: unique_ptr &". Который (мне) несколько запутывает прототипы функций и затрудняет их чтение.
Что лучше для этого? Я пропустил другие варианты?
Ответы
Ответ 1
В современном стиле С++ существуют две концепции ключей:
- собственности
- Недействительность
Собственность касается владельца какого-либо объекта/ресурса (в данном случае экземпляра Device
). Различные std::unique_ptr
, boost::scoped_ptr
или std::shared_ptr
относятся к собственности.
Nullity намного проще: он просто выражает, может ли данный объект быть нулевым, и не заботится ни о чем другом, и, конечно, не о собственности!
Вы были права переместить реализацию своего класса в сторону unique_ptr
(в общем), хотя вам может понадобиться умный указатель с глубокой семантикой копирования, если ваша цель - реализовать PIMPL.
Это ясно говорит о том, что ваш класс несет ответственность за этот кусок памяти и аккуратно занимается всеми различными способами, которые могла бы утечка памяти в противном случае.
С другой стороны, большинству пользователей ресурсов не было все равно, как бы это ни было.
Пока функция не сохраняет ссылку на объект (сохраняет его на карте или что-то еще), тогда все, что имеет значение, - это то, что время жизни объекта превышает продолжительность вызова функции.
Таким образом, выбор способа передачи параметра зависит от его возможного Nullity:
- Никогда не null? Передайте ссылку
- Возможно, null? Передайте указатель, простой пустой указатель или указательный класс (например, с ловушкой на нулевом уровне)
Ответ 2
Это действительно зависит. Если функция должна принадлежать unique_ptr, то она должна иметь значение unique_ptr<Device>
bv , а вызывающий должен std::move
указатель. Если право собственности не является проблемой, я бы сохранил подпись необработанного указателя и передал указатель unique_ptr с помощью get()
. Это не уродливое , если рассматриваемая функция не приобретает права собственности.
Ответ 3
Я бы использовал std::unique_ptr const&
. Использование ссылки non const даст вызываемой функции возможность reset вашего указателя.
Я думаю, что это хороший способ выразить, что ваша вызываемая функция может использовать указатель, но ничего больше.
Поэтому для меня это упростит чтение интерфейса. Я знаю, что мне не нужно возиться с указателем, переданным мне.
Ответ 4
Лучшая практика, вероятно, не использовать std::unique_ptr
в этом случае,
хотя это зависит. (Обычно у вас не должно быть более одного сырья
указатель на динамически выделенный объект в классе. Хотя это
также зависит.) Единственное, что вы не хотите делать в этом случае:
проходя вокруг std::unique_ptr
(и, как вы заметили,
std::unique_ptr<> const&
является немного громоздким и запутывающим). Если это
является единственным динамически выделенным указателем в объекте, я бы просто придерживался
с необработанным указателем и delete
в деструкторе. Если есть
несколько таких указателей, я бы подумал о том, чтобы отнести каждого из них к
отдельный базовый класс (где они все еще могут быть необработанными указателями).
Ответ 5
Это может быть невыполнимо для вас, но замена любого появления Device*
на const unique_ptr<Device>&
- хорошее начало.
Очевидно, вы не можете скопировать unique_ptr
, и вы не хотите его перемещать. Замена ссылкой на unique_ptr
позволяет телу тел существующих функций продолжать работать.
Теперь у вас есть ловушка, вы должны пройти мимо const &
, чтобы препятствовать выполнению вызовов unique_ptr.reset()
или unique_ptr().release()
. Обратите внимание, что это все еще передает изменяемый указатель на устройство. С помощью этого решения у вас нет простого способа передать указатель или ссылку на const Device
.