Когда функция берет shared_ptr
(из boost или C ++ 11 STL), вы передаете ее:
по постоянной ссылке:
void foo(const shared_ptr<T>& p)
или по значению
void foo(shared_ptr<T> p)
:?
Я бы предпочел первый метод, потому что подозреваю, что он будет быстрее. Но стоит ли это того или есть дополнительные проблемы?
Не могли бы вы привести причины вашего выбора или, если дело, почему вы думаете, что это не имеет значения.
c++
c++11
boost
shared-ptr
Danvil
источник
источник
shared_ptr
, и я могу изменить его, если я хочу.», В то время как версия значения говорит: «Я собираюсь скопировать вашshared_ptr
, так что, пока я могу изменить его, вы никогда не узнаете. Параметр const-reference - это реальное решение, которое гласит: «Я собираюсь использовать псевдоним для некоторыхshared_ptr
, и я обещаю не менять его» (что очень похоже на семантику по значению!)shared_ptr
члену класса. Вы делаете это const-refs?Ответы:
Этот вопрос был обсужден и получен ответом Скотта, Андрея и Херба во время сессии « Спросите нас что-нибудь» на C ++ и после 2011 года . Смотрите с 4:34 о
shared_ptr
производительности и правильности .Короче говоря, нет смысла передавать по значению, если только цель не состоит в том, чтобы разделить владение объектом (например, между разными структурами данных или между разными потоками).
Если вы не можете переместить-оптимизировать это, как объяснено Скоттом Мейерсом в видео-ролике, связанном выше, но это связано с реальной версией C ++, которую вы можете использовать.
Главное обновление этой дискуссии произошло во время интерактивной панели конференции GoingNative 2012 : «Спросите нас о чем угодно» который стоит посмотреть, особенно с 22:50 .
источник
Value*
это короткий и читаемый, но это плохо, так что теперь мой код полон,const shared_ptr<Value>&
и он значительно менее читабелен и просто ... менее аккуратен. Что раньше былоvoid Function(Value* v1, Value* v2, Value* v3)
сейчасvoid Function(const shared_ptr<Value>& v1, const shared_ptr<Value>& v2, const shared_ptr<Value>& v3)
, и люди в порядке с этим?class Value {...}; using ValuePtr = std::shared_ptr<Value>;
тогда ваша функция станет проще:void Function(const ValuePtr& v1, const ValuePtr& v2, const ValuePtr& v3)
и вы получите максимальную производительность. Вот почему вы используете C ++, не так ли? :)Вот Херб Саттерс
источник
Лично я бы использовал
const
ссылку. Нет необходимости увеличивать счетчик ссылок, чтобы просто уменьшить его снова для вызова функции.источник
shared_ptr
работает, единственный возможный недостаток, чтобы не перейти по ссылке, это небольшая потеря в производительности. Здесь есть две причины. a) функция наложения указателей означает, что копируется значение данных указателей плюс счетчик (возможно, 2 для слабых ссылок), поэтому копирование данных обходится немного дороже. b) атомарный подсчет ссылок немного медленнее, чем обычный старый код приращения / уменьшения, но необходим для обеспечения безопасности потоков. Кроме того, эти два метода одинаковы для большинства целей и задач.Пройдите по
const
ссылке, это быстрее. Если вам нужно хранить его, скажем, в каком-то контейнере, ссылка. Счет будет автоматически увеличен в результате операции копирования.источник
Я запустил приведенный ниже код, один раз с
foo
принятиемshared_ptr
байта,const&
и снова сfoo
принятиемshared_ptr
значения поИспользование VS2015, сборка x86, на моем процессоре Intel Core 2 Quad (2,4 ГГц)
Версия копирования по значению была на порядок медленнее.
Если вы вызываете функцию синхронно из текущего потока, предпочтите
const&
версию.источник
foo()
функция вообще не должна даже принимать общий указатель, потому что она не использует этот объект: она должна приниматьint&
и делатьp = ++x;
, вызываяfoo(*p);
изmain()
. Функция принимает объект умного указателя, когда ему нужно что-то с ним сделать, и в большинстве случаев вам нужно переместить его (std::move()
) в другое место, поэтому параметр по значению не требует затрат.Начиная с C ++ 11, вы должны воспринимать его по значению над const и чаще, чем вы думаете.
Если вы берете std :: shared_ptr (а не базовый тип T), то вы делаете это, потому что хотите что-то с ним сделать.
Если вы хотите скопировать его куда-то, имеет смысл взять его копией и std :: переместить его внутренне, а не копировать с помощью const &, а затем скопировать позже. Это потому, что вы позволяете вызывающей опции в свою очередь вызывать std :: move shared_ptr при вызове вашей функции, тем самым сохраняя себе набор операций увеличения и уменьшения. Или не. То есть, вызывающая функция может решить, нужен ли ему std :: shared_ptr после вызова функции, и в зависимости от того, перемещать или нет. Это не достижимо, если вы проходите мимо const &, и поэтому желательно, чтобы оно принималось по значению.
Конечно, если вызывающей стороне требуется дольше использовать свой shared_ptr (таким образом, он не может std :: переместить его), и вы не хотите создавать обычную копию в функции (скажем, вам нужен слабый указатель, или вы только иногда хотите скопировать его, в зависимости от некоторых условий), тогда const & все еще может быть предпочтительнее.
Например, вы должны сделать
над
Потому что в этом случае вы всегда создаете копию внутри
источник
Не зная затрат времени на операцию копирования shared_copy, в которой используются атомарные приращения и приращения, я столкнулся с проблемой более высокой загрузки ЦП. Я никогда не ожидал, что приращение атома может привести к таким затратам.
По результатам моего теста, приращение и уменьшение атома int32 занимает в 2 или 40 раз больше, чем неатомарное увеличение и уменьшение. Я получил его на 3GHz Core i7 с Windows 8.1. Первый результат проявляется, когда не возникает спор, а второй - при высокой вероятности возникновения спора. Я имею в виду, что атомарные операции - это, наконец, аппаратная блокировка. Замок это замок. Плохо для производительности, когда происходит конфликт.
Испытывая это, я всегда использую byref (const shared_ptr &), а не byval (shared_ptr).
источник
Был недавний пост в блоге: https://medium.com/@vgasparyan1995/pass-by-value-vs-pass-by-reference-to-const-c-f8944171e3ce
Таким образом, ответ на этот вопрос: (почти) никогда не проходить мимо
const shared_ptr<T>&
.Просто передайте базовый класс.
В основном единственные разумные типы параметров:
shared_ptr<T>
- Изменить и взять на себя ответственностьshared_ptr<const T>
- Не изменяй, бери в собственностьT&
- Изменить, нет собственностиconst T&
- Не модифицируй, не владейT
- Не изменяйте, не владейте, Дешево копироватьКак отметил @accel в https://stackoverflow.com/a/26197326/1930508, совет Херба Саттера:
Но в скольких случаях вы не уверены? Так что это редкая ситуация
источник
Известна проблема, заключающаяся в том, что передача shared_ptr по значению имеет цену, и ее следует избегать, если это возможно.
Стоимость проезда по shared_ptr
Большую часть времени можно было бы передавать shared_ptr по ссылке, а еще лучше по ссылке.
В основном руководстве по cpp есть специальное правило для передачи shared_ptr
R.34: принять параметр shared_ptr, чтобы выразить, что функция является частичным владельцем
Пример, когда передача shared_ptr по значению действительно необходима, - это когда вызывающий объект передает общий объект асинхронному вызываемому объекту, т. Е. Вызывающий объект выходит из области видимости, прежде чем вызываемый объект завершит свою работу. Вызываемый должен «продлить» время жизни разделяемого объекта, приняв share_ptr по значению. В этом случае передача ссылки на shared_ptr не подходит.
То же самое касается передачи общего объекта в рабочий поток.
источник
shared_ptr недостаточно велик, и при этом его конструктор \ деструктор не выполняет достаточной работы для того, чтобы копии было достаточно, чтобы заботиться о передаче по ссылке против производительности копирования.
источник
shared_ptr<int>
значения by занимает более 100 x86 инструкций (включая дорогостоящиеlock
инструкции ed для атомарного включения / определения количества ссылок) Передача константы ref аналогична передаче указателя на что-либо (и в этом примере в проводнике компилятора Godbolt оптимизация хвостового вызова превращает это в простой jmp вместо вызова: godbolt.org/g/TazMBU ).