Что произойдет, если я переустановлю std :: shared_ptr на себя - PullRequest
5 голосов
/ 20 марта 2012

Следующая программа вылетает с ошибкой glibc double free:

#include <iostream>
#include <memory>

class foo {
public:
   foo()
   {
      std::cout << "foo constructed" << std::endl;
   }

   ~foo()
   {
      std::cout << "foo destructed" << std::endl;
   }
};

int main() {
   auto f = std::make_shared< foo >();
   std::cout << "Before reset" << std::endl;
   f.reset( f.get() );
   std::cout << "After reset" << std::endl;
   return 0;
}

Из этого я получаю следующий вывод (за которым следует ошибка glibc):

foo constructed
Before reset
foo destructed
After reset
foo destructed

Итак, очевидно, что в этом случае объект уничтожается дважды. Один раз по сбросу и один раз по std::shared_ptr выходит за рамки. На самом деле это то, чего я ожидал.

Вкл. cppreference , однако я нахожу следующий текст (находится по адресу http://en.cppreference.com/w/cpp/memory/shared_ptr/reset):

Если * он уже владеет объектом и является последним shared_ptr, владеющим им, объект уничтожается посредством принадлежащего ему средства удаления, если ptr не является указателем на него.

По-моему, это фактически говорит о том, что объект не должен быть уничтожен, как в моем примере. Довольно удивительно, но если стандарт так говорит. Я что-то не так понимаю, или имеющаяся у меня реализация std::shared_ptr не соответствует стандарту?

Для тех, кто спрашивает, почему я это делаю:

В настоящее время я пытаюсь выяснить, как временно управлять голыми указателями на объекты, созданные с помощью new и new[]. Идея состоит в том, чтобы использовать std::shared_ptr::reset() для замены средства удаления на неиспользуемое средство удаления. Альтернатива - обернуть код блоком try { stuff() } catch( ... ) { delete x; throw;}.

1 Ответ

8 голосов
/ 20 марта 2012

Спецификация для этой перегрузки reset приведена в 20.7.2.2.4 модификаторах shared_ptr [util.smartptr.shared.mod], параграф 3 (из n3290):

template<class Y> void reset(Y* p);

Эффекты: Эквивалент shared_ptr(p).swap(*this).

Как вы можете видеть, эта конструкция shared_ptr(p) создает новый счетчик с новым удалителем для этого p, поэтому ничего хорошего из этого не выйдет. На самом деле лучше всего думать о std::shared_ptr<T>::get как о строгом наблюдателе, и использование его для управления жизненным циклом является явным признаком того, что происходит что-то не так.

С другой стороны, std::unique_ptr<T> имеет release, что именно то, что вам нужно, когда вам нужно вмешаться и самостоятельно разобраться с собственностью. Возможно, вы можете изменить свой дизайн, чтобы использовать std::unique_ptr<T>? Всегда возможно создать из него std::shared_ptr<T>, если вам нужно, в конце концов. (Хотя std::shared_ptr<T> выбирает средство удаления из std::unique_ptr<T>, вам все равно нужна специальная обработка в случае массива, поскольку вы, вероятно, хотите std::shared_ptr<T*> из std::unique_ptr<T[]>.)

...