Подсчет ссылок с помощью универсального навязчивого указателя клиента - PullRequest
1 голос
/ 06 марта 2012

Введение

Питер Вайнхарт описывает, как спроектировать базовый базовый класс intrusive_ptr с использованием CRTP , который можно использовать следующим образом:

class foo : intrusive_base<foo>
{
     // foo-specific code.
};

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

Концептуально, правильным механизмом для этого сценария является std::shared_ptr.Однако существуют определенные сценарии, требующие необработанных указателей, которые будут вызывать навязчивый указатель, например, при передаче указателей через API C, которые принимают указатели void.В этом случае можно было бы «ref» указатель перед передачей его непрозрачному API и «unref» при его возврате.

Имея контроль над foo, вероятно, лучшим способом было бы использовать политикуна основе реализации и имеют базовую версию с подсчетом ссылок foo.Не имея контроля над foo, альтернативный дизайн мог бы инвертировать отношения наследования:

template <typename Base>
class intrusive : public Base
{
    // ?

private:
    std::atomic_size_t ref_count_;   
};

typedef intrusive<foo> intrusive_foo;

// Assume boost::intrusive_ptr as intrusive pointer implementation
boost::intrusive_ptr<intrusive_foo> x = new intrusive_foo;
{
    auto y = x;   // Semantics: boost::intrusive_ptr_add_ref(x.get())

    // At scope exit: boost::intrusive_ptr_release(x.get())
}

В вышеупомянутой статье Питер говорит, что такая "обобщенная реализация [intrusive] будет использовать C ++ 0x вариационные шаблоны и совершенную пересылку. "

Вопрос

Как будет выглядеть реализация такого универсального класса intrusive?Я мог видеть, что он может извлечь выгоду из наследующих C ++ 11 конструкторов, но мне неясно, как на самом деле можно реализовать тело intrusive, используя упомянутые инструменты.

1 Ответ

0 голосов
/ 06 мая 2012

Использование make_shared дает ту же эффективность, что и навязчивый указатель.

В этом случае указатель «ref» перед передачей его непрозрачному API и «unref» при его возврате.

Как кто-то еще сказал, вы можете использовать enable_shared_from_this, чтобы получить shared_ptr обратно от необработанного указателя (при условии, что где-то в системе есть хотя бы один shared_ptr, которому все еще принадлежит объект)

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

template <typename Base>
  class intrusive : public Base
  {
    template<typename... Args>
      intrusive(Args&&... args)
      : Base(std::forward<Args>(args)...), ref_count_(0)
      { }

Это позволяет вам создать intrusive с любым количеством аргументов любого типа, и они будут перенаправлены на Base, так что вы можете сконструировать его с любыми аргументами, которые можно использовать для создания Base.

Другой альтернативой может быть использование наследующих конструкторов C ++ 11 (которые не реализованы ни в одном компиляторе AFAIK)

template <typename Base>
  class intrusive : public Base
  {
    using Base::Base;
...