Контейнер указателя C ++ с подсчетом ссылок - PullRequest
2 голосов
/ 08 января 2012

Мне нужна коллекция, в которой я могу хранить выделенные в куче объекты, имеющие виртуальные функции.

Я знаю о boost::shared_ptr, std::unique_ptr (C ++ 11) и boost::ptr_(vector|list|map), но они не решают проблему дублирующегося указателя.

Просто, чтобы описать проблему - у меня есть функция, которая принимает выделенный из кучи указатель и сохраняет его для будущего использования:

void SomeClass::add(T* ptr)
{
  _list.push_back(ptr);
}

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

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

Итак, вопрос:

Кто-нибудь знает какую-нибудь библиотеку с коллекциями (вектором, списком, по сути, картой) указателя с автоматическим удалением при уничтожении и поддержкой подсчета ссылок?

Или, может быть, я могу решить эту проблему, используя другую технику?

Обновление:

Мне нужна поддержка дублирующих указателей. Так что я не могу использовать std::set.

Как упомянуто Kerrek SB и Grizzly - плохая идея вообще использовать необработанные указатели и предлагает использовать std::make_shared и забыть об инстанцировании через new. Но это ответственность клиентского кода, а не класса, который я проектирую. Даже если я поменяю add подпись (и, конечно, _list контейнер) на

void SomeClass::add(std::shared_ptr<T> ptr)
{
   _list.push_back(ptr);
}

тогда кто-то (кто не знает о std::make_shared) все еще может написать это:

SomeClass instance;
T* ptr = new T();
instance.add(ptr);
instance.add(ptr);

Так что это не полное решение, которое я жду, но полезно, если вы пишете один код.

Обновление 2:

В качестве альтернативного решения я нашел клонирование (используя конструктор сгенерированных копий). Я имею в виду, что я могу изменить свою add функцию следующим образом:

template <typename R>
void SomeClass::add(const R& ref)
{
  _list.push_back(new R(ref));
}

это позволит виртуальным методам (R - класс, который расширяет некоторый базовый класс (интерфейс)) вызывать и запрещать повторяющиеся указатели. Но у этого решения есть издержки для клона.

Ответы [ 2 ]

1 голос
/ 08 января 2012

Поймите, что умные указатели сравниваются путем сравнения базового контейнера. Таким образом, вы можете просто использовать std::set любого интеллектуального указателя, который вы предпочитаете. Лично я использую std::unique_ptr вместо shared_ptr всякий раз, когда могу сойти с рук, так как это делает владение намного более понятным (тот, кто владеет unique_ptr, является владельцем) и имеет гораздо меньшие накладные расходы. Я обнаружил, что этого достаточно для почти всего моего кода. Код будет выглядеть примерно так:

std::set<std::unique_ptr<T> > _list;

void SomeClass::add(T* ptr)
{
   std::unique_ptr<T> p(ptr);
   auto iter = _list.find(p);
   if(iter == _list.end())
     _list.insert(std::move(p));
   else
     p.release();
}

Я не уверен прямо сейчас, если это излишне (нужно проверить, если insert гарантированно ничего не сделает, если вставка не удалась), но это должно работать. Выполнение этого с shared_ptr<T> будет выглядеть аналогично, хотя и будет немного сложнее из-за отсутствия члена relase. В этом случае я бы, вероятно, сначала сконструировал бы shared_ptr<T> с передачей бездействия deleter для вызова, чтобы найти, а затем другой shared_ptr<T>, который фактически вставлен.

Конечно, лично я бы избегал этого и всегда передавал умные указатели, когда владелец указателя переходил из рук в руки. Поэтому я бы переписал SomeClass::add как void SomeClass::add(std::unique_ptr<T> ptr) или void SomeClass::add(std::shared_ptr<T> ptr), что в любом случае решило бы проблему наличия нескольких экземпляров (при условии, что указатель всегда переносится).

1 голос
/ 08 января 2012

Да: std::list<std::shared_ptr<T>>.

Общий указатель доступен с <memory>, или на старых платформах с <tr1/memory>, или с Boost's <boost/shared_ptr.hpp>.Вам не нужно удалять что-либо вручную, так как совместно используемый указатель позаботится об этом сам.Однако вам нужно будет все указатели кучи внутри общего указателя с самого начала:

std::shared_ptr<T> p(new T);    // legacy
auto p = std::make_shared<T>(); // better

Если вы еще один общий указатель на тот же объект, сделайте копию общегоуказатель (вместо создания нового общего указателя из базового необработанного указателя): auto q = p;


Мораль здесь такова: если вы используете голые указатели, что-то не так.

...