C ++ - передача ссылок на std :: shared_ptr или boost :: shared_ptr

115

Если у меня есть функция, которая должна работать с a shared_ptr, не было бы более эффективным передать ей ссылку на нее (чтобы избежать копирования shared_ptrобъекта)? Каковы возможные побочные эффекты? Я предвижу два возможных случая:

1) внутри функции делается копия аргумента, как в

ClassA::take_copy_of_sp(boost::shared_ptr<foo> &sp)  
{  
     ...  
     m_sp_member=sp; //This will copy the object, incrementing refcount  
     ...  
}  

2) внутри функции используется только аргумент, как в

Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here  
{    
    ...  
    sp->do_something();  
    ...  
}  

Я не вижу в обоих случаях веской причины передавать boost::shared_ptr<foo>по значению, а не по ссылке. Передача по значению только "временно" увеличит счетчик ссылок из-за копирования, а затем уменьшит его при выходе из области действия функции. Я что-то не замечаю?

Чтобы уточнить, после прочтения нескольких ответов: я полностью согласен с проблемами преждевременной оптимизации, и я всегда стараюсь сначала профилировать, а затем работать над горячими точками. Мой вопрос был больше с чисто технической точки зрения, если вы понимаете, о чем я.

abigagli
источник
Я не знаю, можете ли вы изменить теги своего вопроса, но попробуйте добавить туда тег повышения. Я попытался найти этот вопрос, но не смог его найти, потому что искал теги boost и smart-pointer. Итак, я нашел ваш вопрос сразу после того, как составил свой вопрос
Эдисон Густаво Мюнц

Ответы:

113

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

Class::only_work_with_sp(boost::shared_ptr<foo> sp)
{
    // sp points to an object that cannot be destroyed during this function
}

Таким образом, используя ссылку на a shared_ptr, вы отключаете эту гарантию. Итак, во втором случае:

Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here  
{    
    ...  
    sp->do_something();  
    ...  
}

Как узнать, что sp->do_something()не взорвется из-за нулевого указателя?

Все зависит от того, что находится в этих разделах кода «...». Что, если вы вызовете что-то во время первого "...", которое имеет побочный эффект (где-то в другой части кода) очистки shared_ptrэтого же объекта? А что, если это единственное, что остается отличным shared_ptrот этого объекта? До свидания, объект, именно там, где вы собираетесь его использовать.

Итак, есть два способа ответить на этот вопрос:

  1. Очень внимательно изучите источник всей вашей программы, пока не убедитесь, что объект не умрет во время выполнения тела функции.

  2. Измените параметр обратно на отдельный объект, а не на ссылку.

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

Обновление для комментатора JQ

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

У нас есть функция, которая куда-то отправит сообщение. Это может быть большое сообщение, поэтому вместо того, чтобы использовать a, std::stringкоторый, вероятно, копируется, когда он передается в несколько мест, мы используем a shared_ptrдля строки:

void send_message(std::shared_ptr<std::string> msg)
{
    std::cout << (*msg.get()) << std::endl;
}

(Мы просто «отправляем» его в консоль для этого примера).

Теперь мы хотим добавить возможность запоминать предыдущее сообщение. Нам нужно следующее поведение: должна существовать переменная, содержащая самое последнее отправленное сообщение, но пока сообщение отправляется, не должно быть предыдущего сообщения (переменная должна быть сброшена перед отправкой). Итак, мы объявляем новую переменную:

std::shared_ptr<std::string> previous_message;

Затем мы изменяем нашу функцию в соответствии с указанными нами правилами:

void send_message(std::shared_ptr<std::string> msg)
{
    previous_message = 0;
    std::cout << *msg << std::endl;
    previous_message = msg;
}

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

send_message(std::shared_ptr<std::string>(new std::string("Hi")));
send_message(previous_message);

И, как и ожидалось, это печатается Hi!дважды.

Сейчас идет вдоль Г - н Сопровождающий, который смотрит на код и думает: Эй, что параметр send_messageявляется shared_ptr:

void send_message(std::shared_ptr<std::string> msg)

Очевидно, это можно изменить на:

void send_message(const std::shared_ptr<std::string> &msg)

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

Но настоящая проблема в том, что теперь тестовый код будет демонстрировать неопределенное поведение (в отладочных сборках Visual C ++ 2010 он дает сбой).

Мистер Сопровождающий удивлен этим, но добавляет к нему защитную проверку send_message, пытаясь предотвратить возникновение проблемы:

void send_message(const std::shared_ptr<std::string> &msg)
{
    if (msg == 0)
        return;

Но, конечно, он все еще продолжается и вылетает, потому что msgникогда не имеет значения null при send_messageвызове.

Как я уже сказал, когда весь код так близко расположен в тривиальном примере, легко найти ошибку. Но в реальных программах с более сложными отношениями между изменяемыми объектами, которые содержат указатели друг на друга, легко допустить ошибку и сложно построить необходимые тестовые примеры для обнаружения ошибки.

Простое решение, когда вы хотите, чтобы функция могла полагаться на shared_ptrпостоянное значение, отличное от нуля, заключается в том, чтобы функция выделяла собственное значение true shared_ptr, а не полагалась на ссылку на существующий shared_ptr.

Обратной стороной является то, что скопированный a shared_ptrне является бесплатным: даже «безблокировочные» реализации должны использовать заблокированную операцию для соблюдения гарантий многопоточности. Таким образом, могут возникнуть ситуации, когда программу можно значительно ускорить, заменив a shared_ptrна shared_ptr &. Но это изменение не может быть безопасно внесено во все программы. Это меняет логический смысл программы.

Обратите внимание, что аналогичная ошибка возникла бы, если бы мы использовали std::stringповсюду вместо std::shared_ptr<std::string>и вместо:

previous_message = 0;

чтобы очистить сообщение, мы сказали:

previous_message.clear();

Тогда симптомом будет случайная отправка пустого сообщения вместо неопределенного поведения. Стоимость дополнительной копии очень большой строки может быть намного более значительной, чем стоимость копирования shared_ptr, поэтому компромисс может быть другим.

Дэниел Эрвикер
источник
16
Переданный shared_ptr уже находится в области видимости на сайте вызова. Возможно, вы сможете создать сложный сценарий, в котором код в этом вопросе взорвется из-за висячего указателя, но тогда, я полагаю, у вас есть более серьезные проблемы, чем параметр ссылки!
Магнус Хофф
10
Он может храниться в члене. Вы можете вызвать что-то, что происходит, чтобы очистить этот член. Весь смысл smart_ptr состоит в том, чтобы избежать необходимости координировать время жизни в иерархиях или областях, которые гнездятся вокруг стека вызовов, поэтому лучше предположить, что время жизни не делает этого в таких программах.
Дэниел Эрвикер,
7
Но это не совсем моя точка зрения! Если вы думаете, что то, что я говорю, связано с моим кодом, возможно, вы меня не поняли. Я говорю о неизбежном следствии того, что shared_ptr существует в первую очередь: время жизни многих объектов не просто связано с вызовами функций.
Дэниел Эрвикер,
8
@DanielEarwicker полностью согласен со всеми вашими предложениями и удивлен уровнем противодействия. Что-то, что делает ваши опасения еще более актуальными, - это многопоточность, когда это вовлекается, гарантии действительности объектов становятся гораздо более важными. Хороший ответ.
radman
3
Не так давно я обнаружил очень серьезную ошибку, связанную с передачей ссылки на общий указатель. Код обрабатывал изменение состояния объекта, и когда он заметил, что состояние объекта изменилось, он удалил его из коллекции объектов в предыдущем состоянии и переместил в коллекцию объектов в новом состоянии. Операция удаления уничтожила последний общий указатель на объект. Функция-член была вызвана по ссылке на общий указатель в коллекции. Boom. Дэниел Эрвикер прав.
Дэвид Шварц
115

Я обнаружил, что не согласен с ответом, получившим наибольшее количество голосов, поэтому я пошел искать мнения экспертов, и вот они. Из http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2011-Scott-Andrei-and-Herb-Ask-Us-Anything

Херб Саттер: «когда вы передаете shared_ptrs, копии стоят дорого»

Скотт Мейерс: «В shared_ptr нет ничего особенного, когда дело доходит до того, передаете ли вы его по значению или по ссылке. Используйте точно такой же анализ, который вы используете для любого другого определяемого пользователем типа. Люди, похоже, считают, что shared_ptr каким-то образом решает все проблемы управления, и что, поскольку он небольшой, его обязательно недорого передать по значению. Его нужно скопировать, и с этим связаны затраты ... дорого передать его по значению, поэтому, если я могу уйти это с правильной семантикой в ​​моей программе, я передам его по ссылке на const или reference "

Херб Саттер: «всегда передавайте их по ссылке на const, и очень иногда, может быть, потому что вы знаете, что то, что вы вызываете, может изменить то, что у вас есть ссылка, может быть, тогда вы можете передать их по значению ... если вы скопируете их как параметры, о Боже мой, вам почти никогда не нужно увеличивать этот счетчик ссылок, потому что он все равно остается живым, и вы должны передавать его по ссылке, поэтому, пожалуйста, сделайте это "

Обновление: Херб расширил это здесь: http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/ , хотя мораль этой истории заключается в том, что вы не должны проходить мимо shared_ptrs вообще «если вы не хотите использовать или манипулировать самим интеллектуальным указателем, например, для совместного использования или передачи права собственности».

Джон
источник
8
Хорошая находка! Приятно видеть двух ведущих экспертов в этой области, которые публично опровергают общепринятое мнение о SO.
Стефан Толксдорф 01
3
«В shared_ptr нет ничего особенного, когда дело касается того, передаете ли вы его по значению или по ссылке» - я действительно не согласен с этим. Это особенное. Лично я предпочел бы перестраховаться и выдержать небольшое снижение производительности. Если есть конкретная область кода, которую мне нужно оптимизировать, я бы обязательно посмотрел на преимущества в производительности передачи shared_ptr с помощью const ref.
JasonZ
3
Также интересно отметить, что, хотя было достигнуто соглашение о чрезмерном использовании shared_ptrs, не было согласия по вопросу передачи по значению по сравнению с эталоном.
Никол Болас
2
Скотт Мейерс: «Так что, если я смогу обойтись без правильной семантики в моей программе ...» т.е. не противоречит моему ответу вообще, что указывает на то, что выяснить, const &повлияет ли изменение параметров на семантику, легко лишь в очень простые программы.
Дэниел Эрвикер,
7
Херб Саттер: «очень иногда, может быть, потому что вы знаете, что то, что вы назвали, может изменить то, от чего вы получили ссылку». Опять же, это небольшое исключение для незначительного случая, так что это не противоречит моему ответу. Остается вопрос: как узнать , безопасно ли использовать const ref? Очень легко доказать в простой программе, не так просто в сложной программе. Но это же C ++, поэтому мы предпочитаем преждевременную микрооптимизацию почти всем остальным инженерным проблемам, верно ?! :)
Дэниел Эрвикер 08
22

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

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

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

Марк Кегель
источник
Хотя ответ litb технически верен, никогда не стоит недооценивать «лень» программистов (я тоже ленив!). Ответ littlenag лучше, что ссылка на shared_ptr будет неожиданной и, возможно (возможно) ненужной оптимизацией, которая усложнит дальнейшее обслуживание.
netjeff
18

Да, взять ссылку там нормально. Вы не собираетесь передавать методу совместное владение; он только хочет с этим работать. Вы можете взять ссылку и для первого случая, так как вы все равно ее скопируете. Но в первом случае он берет на себя ответственность. Есть способ скопировать его только один раз:

void ClassA::take_copy_of_sp(boost::shared_ptr<foo> sp) {
    m_sp_member.swap(sp);
}

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


Изменить : Конечно, как указывают другие, это верно только в том случае, если вы знаете свой код и знаете, что вы каким-то образом не сбрасываете переданный общий указатель. Если сомневаетесь, просто пройдите по стоимости.

Йоханнес Шауб - litb
источник
11

Разумно пройти shared_ptrмимо const&. Это вряд ли вызовет проблемы (за исключением маловероятного случая shared_ptr, когда указанная ссылка будет удалена во время вызова функции, как подробно описано Earwicker), и, вероятно, будет быстрее, если вы передадите много из них. Помните; по умолчанию boost::shared_ptrпотокобезопасный, поэтому его копирование включает потокобезопасное приращение.

Попробуйте использовать, const&а не просто &, потому что временные объекты не могут передаваться по неконстантной ссылке. (Хотя языковое расширение в MSVC все равно позволяет это делать)

Магнус Хофф
источник
3
Да, я всегда использую константные ссылки, просто забыл вставить это в свой пример. В любом случае, MSVC позволяет привязывать неконстантные ссылки к временным библиотекам не из-за ошибки, а потому, что по умолчанию для свойства «C / C ++ -> Language -> Disable Language Extension» установлено значение «NO». Включите его, и он не будет их компилировать ...
abigagli
Абигагли: Серьезно? Сладкий! Я сделаю это на работе первым делом завтра;)
Магнус Хофф
10

Во втором случае сделать это проще:

Class::only_work_with_sp(foo &sp)
{    
    ...  
    sp.do_something();  
    ...  
}

Вы можете назвать это как

only_work_with_sp(*sp);
Грег Роджерс
источник
3
Если вы примете соглашение об использовании объектных ссылок, когда вам не нужно делать копию указателя, это также послужит документированию вашего намерения. Это также дает вам возможность использовать константную ссылку.
Марк Рэнсом
Да, я согласен с использованием ссылок на объекты как средства выражения того, что вызываемая функция ничего не «запоминает» об этом объекте. Обычно я использую формальные аргументы указателя, если функция «отслеживает» объект
abigagli
3

Я бы избегал "простой" ссылки, если только функция явно не может изменять указатель.

A const &может быть разумной микрооптимизацией при вызове небольших функций - например, для включения дальнейших оптимизаций, таких как встраивание некоторых условий. Кроме того, инкремент / декремент - поскольку он потокобезопасен - является точкой синхронизации. Однако я не ожидал, что это будет иметь большое значение в большинстве сценариев.

Как правило, вам следует использовать более простой стиль, если у вас нет причин не делать этого. Затем либо используйте его const &последовательно, либо добавьте комментарий, почему, если вы используете его только в нескольких местах.

peterchen
источник
3

Я бы поддержал передачу общего указателя по константной ссылке - семантика, согласно которой переданная с указателем функция НЕ владеет указателем, что является чистой идиомой для разработчиков.

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

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

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

Только мои 2 цента :-)

zmqiu
источник
1
См. Комментарий Дэвида Шварца выше: «... Я преследовал очень серьезную ошибку, которая была связана с передачей ссылки на общий указатель. Код обрабатывал изменение состояния объекта, и когда он заметил, что состояние объекта изменилось, он удалил его из коллекции объектов в предыдущем состоянии и переместил в коллекцию объектов в новом состоянии. Операция удаления уничтожила последний общий указатель на объект. Функция-член была вызвана по ссылке на общий указатель в коллекции. Бум ... »
Джейсон Харрисон
3

Кажется, что все плюсы и минусы здесь могут быть обобщены на ЛЮБОЙ тип, передаваемый по ссылке, а не только на shared_ptr. На мой взгляд, вы должны знать семантику передачи по ссылке, константной ссылке и значению и правильно ее использовать. Но нет ничего плохого в передаче shared_ptr по ссылке, если только вы не думаете, что все ссылки плохие ...

Чтобы вернуться к примеру:

Class::only_work_with_sp( foo &sp ) //Again, no copy here  
{    
    ...  
    sp.do_something();  
    ...  
}

Как узнать, что sp.do_something()не взорвется из-за болтающейся стрелки?

Правда в том, что, shared_ptr или нет, const или нет, это может произойти, если у вас есть недостаток дизайна, например, прямое или косвенное разделение прав собственности spмежду потоками, неправильное использование объекта delete this, который имеет, у вас циклическое владение или другие ошибки владения.

Сэнди
источник
2

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

Например, этот код выдаст ошибку, но он будет работать, если вы измените test()так, чтобы общий указатель не передавался по ссылке.

#include <boost/shared_ptr.hpp>

class Base { };
class Derived: public Base { };

// ONLY instances of Base can be passed by reference.  If you have a shared_ptr
// to a derived type, you have to cast it manually.  If you remove the reference
// and pass the shared_ptr by value, then the cast is implicit so you don't have
// to worry about it.
void test(boost::shared_ptr<Base>& b)
{
    return;
}

int main(void)
{
    boost::shared_ptr<Derived> d(new Derived);
    test(d);

    // If you want the above call to work with references, you will have to manually cast
    // pointers like this, EVERY time you call the function.  Since you are creating a new
    // shared pointer, you lose the benefit of passing by reference.
    boost::shared_ptr<Base> b = boost::dynamic_pointer_cast<Base>(d);
    test(b);

    return 0;
}
Malvineous
источник
1

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

Переход по ссылке разрешен

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

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

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

Вы всегда должны избегать хранения своего умного указателя в качестве справочника. Ваш Class::take_copy_of_sp(&sp)пример показывает правильное использование для этого.

Дрю Дорманн
источник
1
«Вы не рискуете потерять указатель из-за использования ссылки. Эта ссылка свидетельствует о том, что у вас есть копия интеллектуального указателя ранее в стеке» Или элемент данных ...?
Дэниел Эрвикер,
Рассмотрим магию boost :: thread и boost :: ref: boost :: function <int> functionPointer = boost :: bind (doSomething, boost :: ref (sharedPtrInstance)); m_workerThread = новый boost :: thread (functionPointer); ... удалить sharedPtrInstance
Джейсон Харрисон
1

Предполагая, что нас не интересует правильность констант (или более того, вы имеете в виду, чтобы позволить функциям иметь возможность изменять или разделять владение передаваемыми данными), передача boost :: shared_ptr по значению безопаснее, чем передача его по ссылке как мы позволяем исходному boost :: shared_ptr управлять своим временем жизни. Рассмотрим результаты следующего кода ...

void FooTakesReference( boost::shared_ptr< int > & ptr )
{
    ptr.reset(); // We reset, and so does sharedA, memory is deleted.
}

void FooTakesValue( boost::shared_ptr< int > ptr )
{
    ptr.reset(); // Our temporary is reset, however sharedB hasn't.
}

void main()
{
    boost::shared_ptr< int > sharedA( new int( 13 ) );
    boost::shared_ptr< int > sharedB( new int( 14 ) );

    FooTakesReference( sharedA );

    FooTakesValue( sharedB );
}

Из приведенного выше примера мы видим, что передача sharedA по ссылке позволяет FooTakesReference сбрасывать исходный указатель, что уменьшает его счетчик использования до 0, уничтожая его данные. Однако FooTakesValue не может сбросить исходный указатель, что гарантирует возможность использования данных sharedB . Когда неизбежно приходит другой разработчик и пытается воспользоваться хрупким существованием sharedA , наступает хаос. Однако удачливый разработчик sharedB уходит домой пораньше, поскольку в его мире все в порядке.

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

Kit10
источник
1

Сэнди пишет: «Кажется, что все плюсы и минусы здесь могут быть обобщены на ЛЮБОЙ тип, передаваемый по ссылке, а не только на shared_ptr».

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

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

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

Билл Х
источник
1

Раньше я работал в проекте, в котором был очень строгий принцип передачи интеллектуальных указателей по значению. Когда меня попросили провести некоторый анализ производительности, я обнаружил, что на увеличение и уменьшение счетчиков ссылок интеллектуальных указателей приложение тратит от 4 до 6% используемого процессорного времени.

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

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

GSF
источник
0

В дополнение к тому, что сказал litb, я хотел бы указать, что во втором примере он, вероятно, будет передаваться по ссылке const, чтобы вы были уверены, что случайно не измените его.

Леон Тиммерманс
источник
0
struct A {
  shared_ptr<Message> msg;
  shared_ptr<Message> * ptr_msg;
}
  1. пройти по значению:

    void set(shared_ptr<Message> msg) {
      this->msg = msg; /// create a new shared_ptr, reference count will be added;
    } /// out of method, new created shared_ptr will be deleted, of course, reference count also be reduced;
  2. пройти по ссылке:

    void set(shared_ptr<Message>& msg) {
     this->msg = msg; /// reference count will be added, because reference is just an alias.
     }
  3. пройти по указателю:

    void set(shared_ptr<Message>* msg) {
      this->ptr_msg = msg; /// reference count will not be added;
    }
Дилан Чен
источник
0

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

В любом случае, даже если функция получает ссылку на константу, а вы «не уверены», вы все равно можете создать копию общего указателя в заголовке функции, чтобы добавить сильную ссылку на указатель. Это также можно рассматривать как намек на дизайн («указатель может быть изменен в другом месте»).

Так что да, ИМО, по умолчанию должно быть « передать по константной ссылке ».

Philippe
источник