Как освободить указатель от boost::shared_ptr?
Может ли boost::shared_ptr освободить сохраненный указатель, не удаляя его?
Я вижу, что в документации нет функции релиза, также в FAQ объясняется, почему она не предоставляет функцию релиза, что-то вроде того, что релиз не может быть сделан по указателям, которые не являются уникальными. Мои указатели уникальны. Как я могу отпустить мои указатели? Или какой повысить класс смарт-указатель использовать, что позволит мне освободить указатель? Я надеюсь, что вы не скажете использовать auto_ptr:)
14 ответов
Вам нужно использовать средство удаления, которое вы можете запросить, чтобы не удалять базовый указатель.
Смотрите этот ответ (который был отмечен как дубликат этого вопроса) для получения дополнительной информации.
Не. FAQ в Boost:
Вопрос Почему shared_ptr не предоставляет функцию release()?
А.shared_ptr не может передать право владения, если он не уникален (), потому что другая копия все равно уничтожит объект.
Рассматривать:
shared_ptr<int> a(new int); shared_ptr<int> b(a); // a.use_count() == b.use_count() == 2 int * p = a.release(); // Who owns p now? b will still call delete on it in its destructor.
Кроме того, указатель, возвращаемый функцией release(), будет трудно надежно освободить, так как источник shared_ptr мог быть создан с помощью пользовательского средства удаления.
Таким образом, это было бы безопасно в случае, если это единственный экземпляр shared_ptr, указывающий на ваш объект (когда unique() возвращает true) и объект не требует специального средства удаления. Я бы все еще сомневался в вашем дизайне, если бы вы использовали такую функцию.release().
Вы могли бы использовать поддельный удалитель. Тогда указатели не будут удалены на самом деле.
struct NullDeleter {template<typename T> void operator()(T*) {} };
// pp of type some_t defined somewhere
boost::shared_ptr<some_t> x(pp, NullDeleter() );
Дети, не делайте этого дома:
// set smarty to point to nothing
// returns old(smarty.get())
// caller is responsible for the returned pointer (careful)
template <typename T>
T* release (shared_ptr<T>& smarty) {
// sanity check:
assert (smarty.unique());
// only one owner (please don't play games with weak_ptr in another thread)
// would want to check the total count (shared+weak) here
// save the pointer:
T *raw = &*smarty;
// at this point smarty owns raw, can't return it
try {
// an exception here would be quite unpleasant
// now smash smarty:
new (&smarty) shared_ptr<T> ();
// REALLY: don't do it!
// the behaviour is not defined!
// in practice: at least a memory leak!
} catch (...) {
// there is no shared_ptr<T> in smarty zombie now
// can't fix it at this point:
// the only fix would be to retry, and it would probably throw again
// sorry, can't do anything
abort ();
}
// smarty is a fresh shared_ptr<T> that doesn't own raw
// at this point, nobody owns raw, can return it
return raw;
}
Теперь, есть ли способ проверить, является ли общее количество владельцев для количества ссылок> 1?
Чтобы указатель снова ни на что не указывал, вы можете позвонить shared_ptr::reset()
,
Однако это удалит объект, на который указывает указатель, когда указатель является последней ссылкой на объект. Это, однако, является именно желаемым поведением интеллектуального указателя в первую очередь.
Если вы просто хотите ссылку, которая не удерживает объект в живых, вы можете создать boost::weak_ptr
(см. документацию повышения). weak_ptr
содержит ссылку на объект, но не добавляет к счетчику ссылок, поэтому объект удаляется, когда существуют только слабые ссылки.
Основой обмена является доверие. Если какой-то экземпляр в вашей программе должен освободить необработанный указатель, почти наверняка shared_ptr
это неправильный тип.
Однако в последнее время я тоже хотел это сделать, поскольку мне нужно было освободиться от другой кучи процессов. В конце концов меня учили, что мое старое решение использовать некоторые std::shared_ptr
не был продуман.
Я просто обычно использовал этот тип для очистки. Но указатель был просто продублирован в нескольких местах. На самом деле мне нужна была std::unique_ptr
который (сюрприз) имеет release
функция.
Прости их, потому что они не знают, что делают. Этот пример работает с boost::shared_ptr и msvs std::shared_ptr без утечек памяти!
template <template <typename> class TSharedPtr, typename Type>
Type * release_shared(TSharedPtr<Type> & ptr)
{
//! this struct mimics the data of std:shared_ptr ( or boost::shared_ptr )
struct SharedVoidPtr
{
struct RefCounter
{
long _Uses;
long _Weaks;
};
void * ptr;
RefCounter * refC;
SharedVoidPtr()
{
ptr = refC = nullptr;
}
~SharedVoidPtr()
{
delete refC;
}
};
assert( ptr.unique() );
Type * t = ptr.get();
SharedVoidPtr sp; // create dummy shared_ptr
TSharedPtr<Type> * spPtr = (TSharedPtr<Type>*)( &sp );
spPtr->swap(ptr); // swap the contents
ptr.reset();
// now the xxx::shared_ptr is empy and
// SharedVoidPtr releases the raw poiter but deletes the underlying counter data
return t;
}
Я не совсем уверен, если ваш вопрос о достижении этого, но если вы хотите поведение от shared_ptr
где, если вы освободите значение от одного shared_ptr
, все другие общие указатели на то же значение становятся nullptr, тогда вы можете поставить unique_ptr
в shared_ptr
достичь такого поведения.
void print(std::string name, std::shared_ptr<std::unique_ptr<int>>& ptr)
{
if(ptr == nullptr || *ptr == nullptr)
{
std::cout << name << " points to nullptr" << std::endl;
}
else
{
std::cout << name << " points to value " << *(*ptr) << std::endl;
}
}
int main()
{
std::shared_ptr<std::unique_ptr<int>> original;
original = std::make_shared<std::unique_ptr<int>>(std::make_unique<int>(50));
std::shared_ptr<std::unique_ptr<int>> shared_original = original;
std::shared_ptr<std::unique_ptr<int>> thief = nullptr;
print(std::string("original"), original);
print(std::string("shared_original"), shared_original);
print(std::string("thief"), thief);
thief = std::make_shared<std::unique_ptr<int>>(original->release());
print(std::string("original"), original);
print(std::string("shared_original"), shared_original);
print(std::string("thief"), thief);
return 0;
}
Выход:
original points to value 50
shared_original points to value 50
thief points to nullptr
original points to nullptr
shared_original points to nullptr
thief points to value 50
Такое поведение позволяет вам совместно использовать ресурс (например, массив), а затем повторно использовать этот ресурс при аннулировании всех общих ссылок на этот ресурс.
Вы можете удалить общий указатель, который мне кажется почти таким же. Если указатели всегда уникальны, то std::auto_ptr<>
хороший выбор Имейте в виду, что уникальные указатели не могут использоваться в контейнерах STL, так как операции с ними делают много копирования и временного дублирования.
Я использую Poco::HTTPRequestHandlerFactory, которая ожидает возврата необработанного HTTPRequestHandler*, платформа Poco удаляет обработчик после завершения запроса.
Также используется проект DI Sauce для создания контроллеров, однако Injector возвращает shared_ptr, который я не могу вернуть напрямую, и возвращение handler.get() также не годится, поскольку, как только эта функция возвращает shared_ptr, выходит из области действия и удаляет затем обработчик перед его выполнением, поэтому есть разумная (я думаю) причина иметь метод.release(). В итоге я создал класс HTTPRequestHandlerWrapper следующим образом:
class HTTPRequestHandlerWrapper : public HTTPRequestHandler {
private:
sauce::shared_ptr<HTTPRequestHandler> _handler;
public:
HTTPRequestHandlerWrapper(sauce::shared_ptr<HTTPRequestHandler> handler) {
_handler = handler;
}
virtual void handleRequest(HTTPServerRequest& request, HTTPServerResponse& response) {
return _handler->handleRequest(request, response);
}
};
и тогда завод
HTTPRequestHandler* HttpHandlerFactory::createRequestHandler(const HTTPServerRequest& request) {
URI uri = URI(request.getURI());
auto path = uri.getPath();
auto method = request.getMethod();
sauce::shared_ptr<HTTPRequestHandler> handler = _injector->get<HTTPRequestHandler>(method + ":" + path);
return new HTTPRequestHandlerWrapper(handler);
}
который удовлетворил и Соус и Poco и работает хорошо.
Мне нужно было передать указатель через асинхронные обработчики и сохранить поведение самоуничтожения в случае сбоя, но конечный API ожидал необработанный указатель, поэтому я сделал эту функцию освобожденной из единственного shared_ptr:
#include <memory>
template<typename T>
T * release(std::shared_ptr<T> & ptr)
{
struct { void operator()(T *) {} } NoDelete;
T * t = nullptr;
if (ptr.use_count() == 1)
{
t = ptr.get();
ptr.template reset<T>(nullptr, NoDelete);
}
return t;
}
Если ptr.use_count() != 1
вы получите nullptr
вместо.
Вот взлом, который может сработать. Я не рекомендовал бы это, если вы не находитесь в реальной привязке.
template<typename T>
T * release_shared(std::shared_ptr<T> & shared)
{
static std::vector<std::shared_ptr<T> > graveyard;
graveyard.push_back(shared);
shared.reset();
return graveyard.back().get();
}
Если ваши указатели действительно уникальны, используйте std::unique_ptr
или же boost::scoped_ptr
если первый не доступен для вашего компилятора. В противном случае рассмотрите возможность объединения использования boost::shared_ptr
с boost::weak_ptr
, Проверьте документацию Boost для деталей.
Простое решение, увеличить ссылку, а затем утечь shared_pointer.
boost::shared_ptr<MyType> shared_pointer_to_instance(new MyType());
new boost::shared_ptr<MyType>();
MyType * raw_pointer = shared_pointer_to_instance.get()
Это явно приведет к утечке памяти как shared_ptr, так и MyType *