виртуальная функция в блоке управления std :: shared_ptr - PullRequest
0 голосов
/ 23 октября 2018

shared::ptr реализации, которые я нашел, написаны таким образом

namespace detail {
   struct deleter_base {
      virtual ~deleter_base() {}
      virtual void operator()( void* ) = 0;
   };
   template <typename T>
   struct deleter : deleter_base {
      virtual void operator()( void* p ) {
         delete static_cast<T*>(p);
      }
   };
}
template <typename T>
class simple_ptr {
   T* ptr;
   detail::deleter_base* deleter;
public:
   template <typename U>
   simple_ptr( U* p ) {
      ptr = p;
      deleter = new detail::deleter<U>();
   }
   ~simple_ptr() {
      (*deleter)( ptr );
      delete deleter;
   }
};

Мои вопросы

1) Почему нам нужны такие структуры (я имею в виду что-то типатехника стирания), разве мы не можем использовать это как средство удаления (см. код ниже)?Какова цель наличия здесь виртуальной функции, как я понимаю, если она не виртуальная, она в любом случае вызовет delete для правильного типа (то есть для Bar type здесь std::shared_ptr<Foo>(new Bar)), потому что simple_ptr имеет шаблонКонструктор.

template <typename T>
   struct deleter {
      void operator()( void* p ) {
         delete static_cast<T*>(p);
      }
   };

2) Зачем нам нужен виртуальный деструктор в базовом классе?Только потому, что он содержит виртуальную функцию или у нашего удалителя тоже должен быть виртуальный деструктор?

Ответы [ 2 ]

0 голосов
/ 27 октября 2018

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

Для любого совместно используемого умного указателя единственная альтернатива для сохранения лямбда-функции до delete static_cast<T*>(p); (или «стирание типа») - это использование удаления для последнего умного указателя-владельца.в группе.

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

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

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

Нельзя создать собственный интеллектуальный указатель, в котором блок управления может быть выделен как часть объекта (как make_shared), если только интеллектуальный указатель не имеет другого типа.

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

Идем по этому пути (избегая затрат на абстрагирование delete p;), блок управления будет меньше, а удаление будет несколько более быстрым, но, вероятно, также приведет к увеличению количества специализированных интеллектуальных указателей, владеющих различными несовместимыми типами , поскольку другие операциичасто будет необходимо на практике.

0 голосов
/ 23 октября 2018

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

Тип уничтоженного уничтожения требуется, если вы хотите иметь возможность преобразовывать общие указатели в T в общие указатели в void, что иногда полезно.В целом, это устраняет необходимость в виртуальном деструкторе хранимых данных.

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

Ваш удалитель, очевидно, не работает;поскольку в нем отсутствует общий базовый класс, его можно хранить только в void*, и вы не можете вызвать void* с (ptr).

Игнорируя ваш shared::ptr, если мы посмотрим нав промышленном качестве std::shared_ptr мы обнаруживаем, что эталонный блок управления хранит удаленное по типу средство удаления в его конце, и если вы make_shared сделаете трюк, чтобы заменить удаленное по типу средство удаления экземпляром объекта.

struct rc_block {
  std::atomic<std::size_t> strong;
  std::atomic<std::size_t> weak;
  virtual void cleanup() = 0;
  virtual ~rc_block() {}
};

template<class T>
struct maked_rc_block final {
  std::atomic<std::size_t> strong = 1;
  std::atomic<std::size_t> weak = 0;
  std::aligned_storage<sizeof(T), alignof(T)> t;
  template<class... Args>
  maked_rc_block(Args&&...args) {
    ::new( (void*)&t ) T(std::forward<Args>(args)...);
  }
  void cleanup() override {
    ((T*)&t)->~T();
  }
};

template<class F>
struct action_rc_block final {
  std::atomic<std::size_t> strong = 1;
  std::atomic<std::size_t> weak = 0;
  F f;
  void cleanup() { f(); }
  template<class IN>
  actoin_rc_block(IN&& in):f(std::forward<IN>(in)) {}
};
template<class F>
action_rc_block(F)->action_rc_block<F>;

template<class T>
struct simple_shared {
  T* ptr = 0;
  rc_block* counters = 0;
  simple_shared( simple_shared const& o ):
    ptr(o.ptr), counters(o.counters)
  { if (counters) ++(counters->strong); }
  ~simple_shared() {
    if (counters && --(counters->strong)) {
      delete counters;
    }
  }
  template<class U>
  simple_shared(U* in):
    ptr(in),
    counters( new action_rc_block{[in]{ delete in; }} )
  {}
  // explicit deleter
  template<class U, class D>
  simple_shared(U* in, D&& d):
    ptr(in),
    counters( new action_rc_block{[in,d=std::forward<D>(d)]{ d(in); }} )
  {}
  template<class U, class V>
  simple_shared(simple_shared<U> const& alias_this, V* v):
    ptr(v),
    counters(alias_this.counters)
  {
    if(counters) ++(counters->strong);
  }
  template<class U>
  simple_shared( maked_rc_block<U>* c ):
    ptr( c?(T*)&c.t:nullptr ),
    counters(c)
  {}
};
template<class T, class...Args>
simple_shared<T> make_simple_shared( Args&&... args ) {
  auto* counter = new make_rc_block<T>( std::forward<Args>(args)... );
  return {counter};
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...