Как освободить указатель от boost :: shared_ptr? - PullRequest
37 голосов
/ 06 октября 2009

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

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

Ответы [ 14 ]

28 голосов
/ 06 октября 2009

Не. Запись часто задаваемых вопросов 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 ().

21 голосов
/ 06 октября 2009

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

struct NullDeleter {template<typename T> void operator()(T*) {} };

// pp of type some_t defined somewhere
boost::shared_ptr<some_t> x(pp, NullDeleter() );
7 голосов
/ 09 октября 2011

Дети, не делайте этого дома:

// 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?

6 голосов
/ 19 мая 2013

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

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

4 голосов
/ 06 октября 2009

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

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

Если вам нужна ссылка, которая не удерживает объект живым, вы можете создать boost::weak_ptr (см. документация для повышения ). weak_ptr содержит ссылку на объект, но не добавляет к счетчику ссылок, поэтому объект удаляется, когда существуют только слабые ссылки.

3 голосов
/ 11 сентября 2012

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

Однако в последнее время я тоже хотел это сделать, так как мне нужно было освободиться из другой кучи процессов. В конце концов меня учили, что мое старое решение использовать немного std::shared_ptr не было продумано.

Я просто обычно использовал этот тип для очистки. Но указатель был просто продублирован в нескольких местах. На самом деле мне нужен std::unique_ptr, который (сюрприз) имеет функцию release.

2 голосов
/ 04 декабря 2012

Прости их, потому что они не знают, что делают. Этот пример работает с 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;
}
1 голос
/ 04 августа 2017

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

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

1 голос
/ 06 октября 2009

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

0 голосов
/ 14 апреля 2017

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

...