Как мне хранить список слабых указателей в фабричном классе?

В моем проекте есть класс Factory, который возвращает std::shared_ptr<MyClass>, если вызывается метод Create. Фабрика не является владельцем, но ведет список созданных объектов. Иногда фабрике необходимо перебрать созданные объекты, которые все еще используются, или подсчитать их.

Я реализовал это, используя std::vector<std::weak_ptr<MyClass>>. Итерация выглядит так:

for (std::weak_ptr<MyClass> wp : weak_list) {
    if (auto sp = wp.lock()) {
        //..
    }
}

Функция Create:

std::shared_ptr<MyClass> Create(/* ... */) {
    std::shared_ptr<MyClass> obj = std::make_shared<MyClass>();

    //...

    weak_list.push_back(obj);
    return obj;
}

Является ли вектор лучшим контейнером для такого списка? Может быть, std::set было бы лучше.

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

Каков предпочтительный способ хранения списка слабых указателей в фабричном классе?


person Iter Ator    schedule 20.08.2017    source источник
comment
ответ всегда будет - это зависит. Вы должны попробовать оба, измерить и выбрать лучший. Не используйте std::make_shared, если вы хотите использовать std::weak_ptr - stackoverflow.com/questions/32113594/   -  person luantkow    schedule 20.08.2017
comment
weak_list это std::vector<std::weak_ptr<MyClass>>. obj shared_ptr возвращается, поэтому я позвонил make_shared   -  person Iter Ator    schedule 20.08.2017
comment
Я понимаю, что вы возвращаете shared_ptr, но сохраняете weak_ptr для нового объекта. Если вы создадите std::shared_ptr с std::make_shared, выделенная память будет освобождена только после уничтожения последнего std::weak_ptr. Это не утечка памяти, но ваше приложение может использовать больше памяти, чем могло бы.   -  person luantkow    schedule 20.08.2017


Ответы (1)


По умолчанию используйте вектор.

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

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

Если вам нужна немедленная очистка оборванных слабых ссылок, измените общий указатель так, чтобы он отменил регистрацию из списка слабых, когда исчезнет последняя сильная ссылка. Это требует некоторой работы с make shared, включая выровненное хранилище, ручное создание и уничтожение, вспомогательную структуру и псевдоним конструктора для общего ptr.

template<class T>
struct shared_helper {
  typename std::aligned_storage<sizeof(T),alignof(T)::type data;
  T* get(){ return reinterpret_cast<T*>(&data); }
  bool constructed = false;
  weak_list<T>* list=0;
  std::weak_ptr<T> self;
  ~shared_helper(){
    if (constructed){
      get()->~T();
      constructed=false;
    }
    if (list) list->erase(self);
  }
  template<class...Ts>
  T* emplace(Ts&&...ts){
    T* r = ::new( (void*)&data ) T(std::forward<Ts>(ts)...);
    constructed = true;
    return r;
  }
};
template<class T, class...Args>
std::shared_ptr<T> make_and_register( weak_list<T>& list, Args&&...args ){
  auto r = std::make_shared<shared_helper<T>>();
  if(!r) return {};
  r->emplace( std::forward<Args>(args)... );
  std::shared_ptr<T> ptr( r, r->get() );
  r->self = ptr;
  list.insert(ptr);
  r->list = &list
  return ptr;
}

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

person Yakk - Adam Nevraumont    schedule 20.08.2017
comment
повторение копии слабого списка (на случай, если итерация изменит слабый список) — да, я испытал странное поведение, не делая этого сам. - person HelloGoodbye; 14.03.2019