Должен ли я передавать shared_ptr по ссылке?

Каковы наилучшие методы для передачи shared_ptr?

В настоящее время я передаю аргументы функции shared_ptr следующим образом:

void function1( shared_ptr<TYPE>& value );

Ответ 1

В контролируемых обстоятельствах вы можете передать общий указатель по постоянной ссылке. Убедитесь, что никто не удаляет объект одновременно, хотя это не должно быть слишком сложно, если вы внимательно относитесь к тому, кому вы даете ссылки.

В общем, вы должны передать общий указатель как прямую копию. Это дает ему намеченную семантику: каждая область, содержащая копию общего указателя, сохраняет объект живым в силу его "доли" в собственности.

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


Дополнительное отступление:

Поскольку на главный вопрос был дан ответ, возможно, поучительно рассмотреть несколько способов использования никогда общего указателя. Вот небольшой мысленный эксперимент. Определим общий тип указателя SF = std::shared_ptr<Foo>. Чтобы рассматривать ссылки, а не передавать аргументы функции, давайте посмотрим на тип RSF = std::reference_wrapper<T>. То есть, если у нас есть общий указатель SF p(std::make_shared<Foo>());, то мы можем создать ссылочную оболочку со значениями семантики через RSF w = std::ref(p);. Так много для настройки.

Теперь все знают, что контейнеры указателей - это минное поле. Таким образом, std::vector<Foo*> станет кошмаром для поддержания, и любое количество ошибок возникает из-за ненадлежащего управления жизненным циклом. Хуже концептуально то, что никогда не ясно, кому принадлежат объекты, указатели которых хранятся в контейнере. Указатели могут представлять собой сочетание указателей на динамические объекты, автоматические объекты и мусор. Никто не может сказать. Поэтому стандартным решением является использование std::vector<SF>. Это правильный способ использования общего указателя. С другой стороны, то, что вы никогда не должны использовать, - это std::vector<RSF> - это неуправляемый монстр, который на самом деле очень похож на оригинальный вектор голых указателей! Например, неясно, жив ли объект, к которому вы держите ссылку. Взятие ссылки на общий указатель нанесло поражение всей цели.

Для второго примера предположим, что у нас есть общий указатель SF p, как и раньше. Теперь у нас есть функция int foo(SF), которую мы хотим запустить одновременно. Обычный std::thread(foo, p) работает просто отлично, поскольку конструктор потока создает копию своих аргументов. Однако, если бы мы сказали std::thread(foo, std::ref(p)), у нас были бы всевозможные проблемы: общий указатель в области вызова может истечь и уничтожить объект, и вы останетесь с оборванной ссылкой и недопустимым указателем!

Я надеюсь, что эти два, по общему признанию, довольно надуманные примеры проливают немного света, когда вы действительно хотите, чтобы ваши общие указатели были переданы копией. В хорошо продуманной программе всегда должно быть понятно, кто несет ответственность за какие ресурсы, а при правильном использовании общий указатель - отличный инструмент для работы.

Ответ 2

Это зависит от того, что вы хотите. Должна ли эта группа владеть объектом? Тогда ему нужна собственная копия shared_ptr. Поэтому передайте его по значению.

Если функции просто нужно получить доступ к объекту, принадлежащему вызывающему абоненту, перейдите по ссылке (const), чтобы избежать накладных расходов на копирование shared_ptr.

Лучшая практика на С++ всегда должна иметь четко определенную семантику собственности для ваших объектов. Нет универсального "всегда делать это", чтобы заменить реальную мысль.

Если вы всегда передаете общие указатели по значению, это становится дорогостоящим (потому что их намного дороже, чем исходный указатель). Если вы никогда не выполняете это, тогда нет смысла использовать общий указатель в первую очередь.

Скопируйте общий указатель, когда новая функция или объект должны передать права собственности на pointee.

Ответ 3

Передайте его по ссылке const, если вы пройдете по ссылке. Это дает понять, что вы переходите по ссылке по соображениям производительности. Кроме того, используйте make_shared, когда сможете, так как он сохраняет косвенность, поэтому дает повышение уровня.