2012-03-14 5 views
45

Я пытаюсь «модернизировать» существующий код.Передача unique_ptr в функции

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

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

Мой вопрос заключается в следующем -

  • Как я должен затем передать устройство _ переменной для всех функций, которые нуждаются в ней в качестве В параметре?

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

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

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

+3

Почему бы это изменение сделать код более безопасным и вообще лучше? Из вашего описания я бы сказал, что все наоборот. –

+0

@James Kanze Я согласен, unique_ptr, похоже, ничего не покупает в этом случае. – juanchopanza

+2

Вы вполне можете быть правы. Мне нравится «безопасность» unique_ptr в том, что вам не нужно забывать его удалять, но в этом случае у него есть определенная стоимость. – jcoder

ответ

44

В современном стиле C++, есть две клавиши понятия:

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

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

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


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

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


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

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

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

  • Никогда нулевой? Pass a ссылка
  • Возможно, null?Проходят указатель, простой голый указатель или указатель типа класса (с ловушкой на нуль, например)

+0

Хмм, ссылки ... Я мог бы выполнить мои функции с помощью параметров «Устройство и устройство», а затем в вызывающем коде использовать * device_, поскольку это все равно будет работать с unique_ptr, и я знаю, что device_ не может быть нулевым (и может проверить это с помощью утверждений). Теперь мне нужно подумать об этом немного и решить, неужели это уродливо или элегантно! – jcoder

+2

@JohnB: Ну, моя биаизированная точка зрения заключается в том, что искоренение возможности недействительности во время компиляции намного безопаснее :) –

+6

@JohnB: Полностью согласен с Matthieu здесь, и я бы даже пошел дальше, почему вы хотите динамически выделить объект, чье время жизни привязано к времени жизни объекта, содержащего указатель? Почему бы не объявить его простым членом с автоматическим хранилищем? –

7

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

+0

Чтобы быть ясным, добавление константы будет означать, что я никак не могу изменить unique_ptr, но я * могу * вызывать не const-функции содержащегося объекта через unique_ptr? – jcoder

+0

@JohnB: Да, функция const запрещает вызов вызываемой функции, например, сброс на ваш уникальный_ptr. Но до тех пор, пока указатель внутри не const, вы можете вызывать функции non const. – mkaes

+0

Да, вы можете. get() const метод unique_ptr возвращает неконстантный указатель на объект, охраняемый unique_ptr – zabulus

1

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

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

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

+0

Поскольку const unique_ptr & является громоздким, я бы набрал значение для Device_t или аналогичного. –

14

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

+0

Да, я не хочу брать собственное владение, просто используйте указатель на объект в функции. – jcoder

+2

Вы также можете передать ссылку const на unique_ptr, если право собственности не является проблемой. Некоторые люди используют дихотомию с указателем-ссылкой, чтобы имитировать семантику формальных параметров входного параметра Паскаля (руководство по стилю Google C++). –

2

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

Смежные вопросы