Передача unique_ptr в функции - PullRequest
46 голосов
/ 14 марта 2012

Я пытаюсь "модернизировать" некоторый существующий код.

  • У меня есть класс, который в настоящее время имеет переменную-член "Device * device_".
  • Он использует new для создания экземпляра в некотором коде инициализации и имеет "delete device_" в деструктуре.
  • Функции-члены этого класса вызывают многие другие функции, которые принимают Device * в качестве параметра.

Это хорошо работает, но для "модернизации" моего кодаЯ подумал, что мне следует изменить переменную, которая будет определена как "std::unique_ptr<Device> device_", и удалить явный вызов delete, что делает код более безопасным и в целом лучше.

Мой вопрос такой -

  • Как мне тогда передать переменную device _ всем функциям, которым она нужна в качестве параметра?

Я могу вызвать .get, чтобы получить необработанный указатель в каждомвызов функции.Но это кажется уродливым и тратит впустую некоторые причины использовать unique_ptr.

Или я могу изменить каждую функцию так, чтобы вместо принятия параметра типа «Устройство *»теперь он принимает параметр типа "std :: unique_ptr &".Который (для меня) несколько запутывает прототипы функций и затрудняет их чтение.

Что лучше для этого делать?Я пропустил другие варианты?

Ответы [ 5 ]

44 голосов
/ 14 марта 2012

В Современный C ++ стиль, есть две ключевые концепции:

  • Владение
  • Недействительность

Владение о владельце какого-либо объекта / ресурса (в данном случае экземпляр Device).Различные std::unique_ptr, boost::scoped_ptr или std::shared_ptr относятся к владению.

Nullity , однако, гораздо проще: он просто выражает, может ли данный объект быть нулевым, ине заботится ни о чем другом, и уж точно не о праве собственности!


Вы были правы , чтобы переместить реализацию вашего класса в сторону unique_ptr (в целом), хотя вы можетевам нужен умный указатель с глубокой семантикой копирования, если ваша цель - реализовать PIMPL.

Это ясно говорит о том, что ваш класс является единоличным ответственным за этот фрагмент памяти и аккуратно разбирается со всеми различными способами утечки памятив противном случае.


С другой стороны, большинство пользователей ресурсов могут не заботиться о его владении.

Пока функция не сохраняетссылка на объект (сохраните его на карте или чем-то еще), тогда все, что имеет значение, это то, что время жизни объекта превышает продолжительность вызова функции.

Таким образом, выбираякак передать параметр зависит от его возможного Nullity :

  • Never null?Передайте ссылку
  • Возможно, ноль?Передайте указатель , простой пустой указатель или класс, подобный указателю (например, с ловушкой на нуле)

14 голосов
/ 14 марта 2012

Это действительно зависит. Если функция должна вступить во владение unique_ptr, тогда ее подпись должна принимать значение unique_ptr<Device> bv , а вызывающая сторона должна std::move указатель. Если владение не является проблемой, то я бы оставил подпись необработанного указателя и передала бы указатель unique_ptr, используя get(). Это не безобразно , если рассматриваемая функция не переходит во владение.

7 голосов
/ 14 марта 2012

Я бы использовал std::unique_ptr const&. Использование неконстантной ссылки даст вызываемой функции возможность сброса указателя.
Я думаю, что это хороший способ выразить, что ваша вызываемая функция может использовать указатель, но не более того.
Так что для меня это облегчит чтение интерфейса. Я знаю, что мне не нужно возиться с указателем, переданным мне.

2 голосов
/ 14 марта 2012

Лучше всего не использовать std::unique_ptr в этом случае, хотя это зависит.(Как правило, у вас не должно быть более одного необработанного указателя на динамически размещенный объект в классе. Хотя это также зависит.) Единственное, что вы не хотите делать в этом случае, это обойти std::unique_ptr (и когда вызаметил, что std::unique_ptr<> const& немного громоздко и запутанно).Если это единственный динамически размещенный указатель в объекте, я бы просто придерживался необработанного указателя и delete в деструкторе.Если таких указателей несколько, я бы рассмотрел вопрос о переводе каждого из них в отдельный базовый класс (где они все еще могут быть необработанными указателями).

1 голос
/ 14 марта 2012

Это может быть неосуществимо для вас, но замена каждого случая Device* на const unique_ptr<Device>& - хорошее начало.

Вы, очевидно, не можете скопировать unique_ptr s и не хотитепереместить это.Замена ссылкой на unique_ptr позволяет телу существующих функций продолжать работать.

Теперь есть ловушка, вы должны пройти мимо const &, чтобы запретить вызываемым абонентам unique_ptr.reset() или unique_ptr().release().Обратите внимание, что это все еще передает изменяемый указатель на устройство.С этим решением у вас нет простого способа передать указатель или ссылку на const Device.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...