Ответ 1
Вам нужно использовать дебетер, который вы можете запросить, чтобы не удалить основной указатель.
См. этот ответ (который был отмечен как дубликат этого вопроса) для получения дополнительной информации.
Может ли boost:: shared_ptr освобождать сохраненный указатель без его удаления?
Я вижу, что в документации отсутствует функция release, также в FAQ часто объясняется, почему она не предоставляет функцию выпуска, что-то вроде того, что релиз не может быть выполнен по указателям, которые не уникальны. Мои указатели уникальны. Как я могу освободить свои указатели? Или, что стимулирует класс интеллектуального указателя, который позволит мне освободить указатель? Я надеюсь, что вы не будете использовать auto_ptr:)
Вам нужно использовать дебетер, который вы можете запросить, чтобы не удалить основной указатель.
См. этот ответ (который был отмечен как дубликат этого вопроса) для получения дополнительной информации.
не делать. Boost FAQ:
Q. Почему 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
(см. ускорить документирование). A weak_ptr
содержит ссылку на объект, но не добавляет к счетчику ссылок, поэтому объект удаляется, когда существуют только слабые ссылки.
Основой разделения является доверие. Если какой-то экземпляр вашей программы должен освободить необработанный указатель, почти наверняка будет shared_ptr
неправильный тип.
Однако в последнее время я тоже хотел это сделать, поскольку мне нужно было освободиться от другого процесса-кучи. В конце концов меня учили, что мое прежнее решение использовать некоторые std::shared_ptr
не было продумано.
Я просто использовал этот тип для очистки. Но указатель был просто дублирован на несколько мест. На самом деле мне понадобился std::unique_ptr
, который (suprise) имеет функцию 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;
}
Вы можете удалить общий указатель, который кажется мне таким же. Если указатели всегда уникальны, то std::auto_ptr<>
- хороший выбор. Имейте в виду, что уникальные указатели не могут использоваться в контейнерах STL, поскольку операции с ними делают много копий и временное дублирование.
Вот ручка, которая может работать. Я бы не рекомендовал его, если у вас нет реальной привязки.
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 documentation.
Я использую 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);
}
};
а затем factory будет
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);
}
которые удовлетворяли как Соус, так и Поко и прекрасно работали.
Мне нужно было передать указатель через асинхронные обработчики и сохранить поведение самоуничтожения в случае сбоя, но конечный 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
.
Я не совсем уверен, что ваш вопрос заключается в достижении этого, но если вам нужно поведение от 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
Такое поведение позволяет вам совместно использовать ресурс (например, массив), а затем повторно использовать этот ресурс, недействив все общие ссылки на этот ресурс.
Простое решение, увеличьте ссылочку и затем пропустите 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 *