Должны ли мы передавать shared_ptr по ссылке или по значению? - PullRequest
229 голосов
/ 22 июля 2010

Когда функция принимает shared_ptr (от boost или C ++ 11 STL), вы передаете ее:

  • по константной ссылке: void foo(const shared_ptr<T>& p)

  • или по значению: void foo(shared_ptr<T> p)?

Я бы предпочел первый метод, потому что подозреваю, что он будет быстрее.Но действительно ли это того стоит или есть какие-то дополнительные проблемы?

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

Ответы [ 8 ]

195 голосов
/ 05 января 2012

Этот вопрос был обсужден и получен ответом Скотта, Андрея и Херба во время Спросите нас что-нибудь сеанс на C ++ и после 2011 .Наблюдайте с 4 минут 34 секунды на shared_ptr производительности и корректности .

Вскоре нет причин переходить на значение , если только целью не является разделение права собственности наобъект (например, между различными структурами данных или между различными потоками).

Если только вы не можете перемещать-оптимизировать его, как объяснено Скоттом Мейерсом в видео разговора, связанном выше, но это связано с фактической версией C ++Вы можете использовать.

Главное обновление этого обсуждения произошло во время GoingNative 2012 Конференция Интерактивная панель: «Спросите нас что-нибудь!» , которую стоит посмотреть, особенно с 22: 50 * 1 021 * * * 1 022.

72 голосов
/ 05 октября 2014

Вот Взятие Херба Саттера

Указание: не передавайте смарт-указатель в качестве параметра функции, если вы не хотите использовать или манипулировать самим смарт-указателем, например, дляподелиться или передать право владения.

Указание: укажите, что функция будет хранить и совместно использовать владение объектом кучи, используя параметр shared_ptr по значению.

Указание: используйте неконстантный параметр shared_ptr & только для изменения shared_ptr.Используйте const shared_ptr & в качестве параметра только в том случае, если вы не уверены, будете ли вы брать копию и передавать ее в собственность;в противном случае используйте вместо него виджет * (или, если не обнуляемый, виджет &).

58 голосов
/ 22 июля 2010

Лично я бы использовал ссылку const.Нет необходимости увеличивать счетчик ссылок, чтобы просто уменьшить его снова для вызова функции.

32 голосов
/ 22 июля 2010

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

19 голосов
/ 26 августа 2015

Я запустил приведенный ниже код: один раз с foo взяв shared_ptr на const& и снова с foo, взяв shared_ptr по значению.

void foo(const std::shared_ptr<int>& p)
{
    static int x = 0;
    *p = ++x;
}

int main()
{
    auto p = std::make_shared<int>();
    auto start = clock();
    for (int i = 0; i < 10000000; ++i)
    {
        foo(p);
    }    
    std::cout << "Took " << clock() - start << " ms" << std::endl;
}

Использование VS2015, сборка x86, на моем процессоре Intel Core 2 Quad (2,4 ГГц)

const shared_ptr&     - 10ms  
shared_ptr            - 281ms 

Версия копирования по значению была на порядок медленнее.
Если вы вызываете функцию синхронно из текущего потока, предпочтите версию const&.

11 голосов
/ 21 марта 2017

Начиная с C ++ 11, вы должны принимать его на величину больше, чем const & чаще, чем вы думаете.

Если вы используете std :: shared_ptr (а не базовый тип T), то вы делаете это, потому что хотите что-то с ним сделать.

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

Конечно, если вызывающей стороне требуется дольше использовать свой shared_ptr (таким образом, он не может std :: переместить его), и вы не хотите создавать обычную копию в функции (скажем, вам нужен слабый указатель, или вы только иногда хочется скопировать его, в зависимости от каких-либо условий), тогда const & может быть предпочтительнее.

Например, вы должны сделать

void enqueue(std::shared<T> t) m_internal_queue.enqueue(std::move(t));

над

void enqueue(std::shared<T> const& t) m_internal_queue.enqueue(t);

Потому что в этом случае вы всегда создаете копию внутри

1 голос
/ 18 апреля 2016

Не зная затрат времени на операцию копирования shared_copy, в которой используется атомарный прирост и декремент, я страдал от гораздо более высокой проблемы использования ЦП.Я никогда не ожидал, что приращение и уменьшение атома может потребовать столь больших затрат.

По результатам моего теста приращение и уменьшение атома int32 занимает в 2 или 40 раз больше, чем неатомарное увеличение и уменьшение.Я получил его на 3GHz Core i7 с Windows 8.1.Первый результат проявляется, когда не возникает спор, а второй - при высокой вероятности возникновения спора.Я имею в виду, что атомарные операции - это, наконец, аппаратная блокировка.Замок это замок.Плохая производительность при возникновении конфликтов.

Из-за этого я всегда использую byref (const shared_ptr &), а не byval (shared_ptr).

0 голосов
/ 22 июля 2010

shared_ptr недостаточно велик, и его конструктор \ деструктор не выполняет достаточно работы для того, чтобы в копии было достаточно служебных данных, чтобы заботиться о передаче по ссылке против производительности копирования.

...