Re: void f(boost::shared_ptr<X> const& p);
От: remark Россия http://www.1024cores.net/
Дата: 29.12.09 09:59
Оценка: 12 (1)
Здравствуйте, 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 раза? Разве вызывающий код может освободить счётчик до возврата из функции?


  • 1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.