Судя по вашему выводу Valgrind, проблема в том, что объект, на который указывает shared_ptr
, удаляется дважды. Одна возможность получить это, если вы инициализируете два shared_ptr
с одним и тем же необработанным указателем, например ::
int* p = new int(123);
shared_ptr<int> sp1(p);
shared_ptr<int> sp2(p);
shared_ptr
не является магическим, и он не может знать, принадлежит ли объект, о котором вы просите, чтобы о нем позаботились, другим не связанным shared_ptr
, если все, что вы даете, это необработанный указатель. В приведенном выше примере каждый shared_ptr
создаст свой собственный счетчик ссылок, инициализированный 1; когда они умирают, каждый уменьшает свой счетчик, видит, что он равен 0, и удаляет объект, производя двойное удаление. Я подозреваю, что ваш случай похож. Если вы показываете код, используемый для инициализации shared_ptr
объектов, которые добавляются в вектор, это можно проверить.
[РЕДАКТИРОВАТЬ] Так как это подтверждено, чтобы быть причиной сбоя сейчас, позвольте мне разработать
немного о том, как правильно использовать shared_ptr
.
Прежде всего, природа проблемы. Способ shared_ptr
написан, он работает с любым типом C ++ и обеспечивает семантику подсчета ссылок. Очевидная проблема состоит в том, что большинство типов не предоставляют места для хранения счетчика ссылок (например, рассмотрим shared_ptr<int>
- нет дополнительного пространства "внутри" int
). Чтобы обойти это, для каждого общего объекта выделяется отдельный блок памяти, который содержит счетчик ссылок. Это делается всякий раз, когда вы создаете shared_ptr
из необработанного указателя. Затем сам объект shared_ptr
хранит исходный необработанный указатель и указатель на счетчик ссылок (именно поэтому он более «толстый», чем необработанный указатель, который можно тривиально проверить с помощью sizeof
). Когда вы создаете один shared_ptr
из другого (используя конструктор копирования или оператор присваивания), он копирует указатель на счетчик ссылок, поэтому все экземпляры shared_ptr
, созданные друг от друга, поддерживают один счетчик и гарантируют правильное удаление. Но если у вас есть два несвязанных «семейства» shared_ptr
объектов для одних и тех же объектов (где два или более указателей были созданы из одного и того же необработанного указателя), эти «семейства» не будут знать друг о друге и будут пересчитываться отдельно, и каждый удалит, когда он достигнет 0.
На практике это означает, что при использовании shared_ptr
необходимо придерживаться определенных правил. Они зависят от того, какую реализацию вы используете.
В std::tr1::shared_ptr
или более старых версиях Boost единственный полностью безопасный шаблон для выделения объектов - это:
shared_ptr<T> x(new T(...));
Другими словами, результат new
должен быть немедленно помещен в shared_ptr
- вы можете скопировать последнее столько раз, сколько захотите.
Достаточно безопасный шаблон также такой:
auto_ptr<T> x(new T);
...
shared_ptr<T> y(x);
shared_ptr
реализует обычную передачу права собственности при инициализации с auto_ptr
, а семантика последнего (при условии, что они правильно соблюдаются) обеспечивает существование только одного auto_ptr
для объекта; таким образом, можно сконструировать shared_ptr
из этого.
Иногда вам также приходится иметь дело с библиотеками C ++, которые не используют auto_ptr
для указания передачи владения указателем, а просто документируют намерение для определенных функций. В этих случаях также может быть безопасно использовать shared_ptr
, но, конечно, вы должны быть уверены, что правильно поняли документацию ...
В C ++ 0x std::shared_ptr
и в более новых версиях boost::shared_ptr
есть помощник , предоставленный для обеспечения правильной реализации общих объектов:
shared_ptr<int> p = make_shared<int>(123);
Тип возвращаемого значения make_shared<T>()
уже равен shared_ptr<T>
, поэтому ни в коем случае вы не имеете дело с необработанными указателями в своем коде, что снижает вероятность ошибиться.