Вопрос

Может ли boost::shared_ptr освободить сохраненный указатель, не удаляя его?

Я вижу, что в документации не существует функции выпуска, также в FAQ объясняется, почему она не предоставляет функцию выпуска, что-то вроде того, что выпуск не может быть выполнен по указателям, которые не являются уникальными.Мои указатели уникальны.Как я могу освободить свои указатели?Или какой класс boost smart pointer использовать, который позволит мне освободить указатель?Я надеюсь, что вы не скажете использовать auto_ptr :)

Это было полезно?

Решение

Вам нужно использовать средство удаления, которое вы можете запросить, чтобы не удалять базовый указатель.

Смотрите этот ответ (который был помечен как дубликат этого вопроса) для получения дополнительной информации.

Другие советы

Не надо.Запись часто задаваемых вопросов Boost:

Q.Почему shared_ptr не предоставляет функцию release()?

A. 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;
}

Итак, есть ли способ проверить, равно ли общее количество владельцев для ref count > 1?

Чтобы указатель снова ни на что не указывал, вы можете вызвать shared_ptr::reset().

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

Если вам просто нужна ссылка, которая не содержит активного объекта, вы можете создать boost::weak_ptr (см. расширенная документация).A 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;
}

Вы можете удалить общий указатель, который мне кажется почти таким же.Если указатели всегда уникальны, то std::auto_ptr<> это хороший выбор.Имейте в виду, что уникальные указатели нельзя использовать в контейнерах STL, поскольку операции с ними приводят к большому копированию и временному дублированию.

Я не совсем уверен, касается ли ваш вопрос достижения этого, но если вы хотите, чтобы поведение от 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

Такое поведение позволяет вам совместно использовать ресурс (например, массив), а затем повторно использовать этот ресурс, делая недействительными все общие ссылки на этот ресурс.

Вот хак, который может сработать.Я бы не рекомендовал этого делать, если только вы не попали в настоящий переплет.

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.Ознакомьтесь с Расширенная документация для получения подробной информации.

Я использую Poco::HTTPRequestHandlerFactory, который ожидает вернуть необработанный HTTPRequestHandler *, платформа Poco удаляет обработчик после завершения запроса.

Также использую проект DI Sauce для создания контроллеров, однако инжектор возвращает 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);
}

который удовлетворяет и Соусу, и Поко и прекрасно сочетается.

Мне нужно было передать указатель через асинхронные обработчики и сохранить поведение самоуничтожения в случае сбоя, но конечный 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_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 *

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top