Здравствуйте, Alexander G, Вы писали:
AG>Стоит ли передавать boost::shared_ptr<X> по константной ссылке?
AG>С одной стороны, подсчёт ссылок может дорого стоить.
AG>но недостатков много:
AG> Добавляется косвенность (если ссылки реализованы как указатели).
Если приложение многопоточное (для однопоточного там вроде был какой-то макрос для shared_ptr, который убирал атомарные операции из shared_ptr), то стоит. Это покроет дополнительный уровень косвенности.
AG> Больше букв писать.
Ну с контейнерами же мы терпим
На крайняк можно сделать что-то типа:
typedef std::string const& str_p;
struct x {...};
typedef shared_ptr<x> x_ptr;
typedef shared_ptr<x> const& x_ptr_p;
AG> const ссылка может создавать иллюзию константности.
AG> Оно хорошо только если типы совпадают, иначе — преобразование всё равно нужно.
AG>AG>class Y : X {};
AG>void f(boost::shared_ptr<X const> const& p);
AG>boost::shared_ptr<Y const> p1;
AG>boost::shared_ptr<X> p2;
AG>f(p1); // временый boost::shared_ptr<X const> создаётся
AG>f(p2); // временый boost::shared_ptr<X const> создаётся
AG>
AG>Как вообще бороться с оверхедом на подсчёт ссылок и стоит ли?
*Может* стоить. Стоит ли в конкретном случае — скажет только профайлер.
Как бороться — тут есть 2 пути: консервативный и оперативный.
Консервативный — это убрать лишние операции над счётчиком ссылок (которых может быть великое множество). Во-первых, конечно, не передавать по значению. Во-вторых, не возвращать по значению. В-третьих, не делать лишних копий на стеке.
Вообще, изменение счётчика необходимо только в 2 случаях — кладём в динамический объект, берем из динамического объекта. Всё, что касается стека, вызова функций и т.д. тут необходимости менять счётчик нет.
В-четвёртых, не использовать подсчёт ссылок там, где управление временем жизни тривиально и без него. Например некоторые любят использовать shared_ptr в таких вещах, как очереди производитель-потребитель и многопоточные хэш-мапы; тут тривиально реализовать без него — производитель создал объект, положил в очередь, потребитель взял объект из очереди, обработал, удалил (пока объект живёт у производителя и потребителя его можно защитить auto_ptr).
Если консервативное лечение не помогает, то оперативный способ — использовать другую схему управления временем жизни. Вообще говоря, атомарный подсчёт ссылок — анти-паттерн масштабируемости.
AG>Кстати, в Delphi для типов, для которых подсчёт ссылок встроен в компилятор, всё просто и без увеличения уровня косвенности:
В С++ это тоже тривиально — используй интрузивный умный указатель (boost::intrusive_ptr). Если производительность важна, то неинтрузивный shared_ptr имеет смысл использовать только для прототипирования; а для реального кода — интрузивный (такая же история и с контейнерами — boost::intrusive).
При использовании интрузивного подсчёта ссылок в функцию можно передавать обычный указатель на объект, а умный указатель всегда можно из него восстановить при необходимости.
AG>AG>procedure P1(S: string; I: IUnknown);
AG>begin (* неявное увеличение числа сссылок на S и AddRef для I *)
AG> S := F1(); I := F2(); (* можно *)
AG>end; (* неявное уменьшение числа сссылок на S и Release для I *)
AG>
Зачем при обычном вызове функции надо менять счётчик 2 раза? Разве вызывающий код может освободить счётчик до возврата из функции?