web-dev-qa-db-fra.com

Comment libérer le pointeur de boost :: shared_ptr?

Boost :: shared_ptr peut-il libérer le pointeur stocké sans le supprimer?

Je constate qu’il n’existe pas de fonction de libération dans la documentation. La FAQ explique également pourquoi elle ne fournit pas de fonction de publication. Par exemple, la publication ne peut pas être effectuée sur des pointeurs non uniques. Mes pointeurs sont uniques. Comment puis-je libérer mes pointeurs? Ou quelle classe de pointeur intelligent à utiliser pour me permettre de relâcher le pointeur? J'espère que vous ne direz pas utiliser auto_ptr :)

36
user152508

Vous devez utiliser un suppresseur que vous pouvez demander pour ne pas supprimer le pointeur sous-jacent.

Voir cette réponse (qui a été marquée comme un doublon de cette question) pour plus d'informations.

6
Martin Ba

Ne pas L'entrée FAQ de Boost:

Q. Pourquoi shared_ptr ne fournit-il pas une fonction release ()?

UNE. shared_ptr ne peut céder la propriété que si elle est unique () car l'autre copie détruira quand même l'objet.

Considérer:

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.

De plus, le pointeur renvoyé par release () serait difficile à désallouer de manière fiable, car la source shared_ptr aurait pu être créée avec un deleter personnalisé.

Donc, cela serait sûr si c'est la seule instance shared_ptr pointant sur votre objet (lorsque unique () renvoie True) et que l'objet ne nécessite pas de suppresseur spécial. Je remettrais tout de même votre conception en question si vous utilisiez une telle fonction .release ().

28
sellibitze

Vous pouvez utiliser de faux deleter. Les pointeurs ne seront alors pas supprimés.

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

// pp of type some_t defined somewhere
boost::shared_ptr<some_t> x(pp, NullDeleter() );
20

Les enfants, ne faites pas ça à la maison:

// 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;
}

Maintenant, y at-il un moyen de vérifier si le nombre total de propriétaires pour le nombre de réf est supérieur à 1?

7
curiousguy

Pour que le pointeur ne pointe plus vers rien, vous pouvez appeler shared_ptr::reset().

Toutefois, cela supprimera l'objet pointé lorsque votre pointeur est la dernière référence à l'objet. Ceci, cependant, est exactement le comportement souhaité du pointeur intelligent en premier lieu.

Si vous voulez juste une référence qui ne maintienne pas l'objet en vie, vous pouvez créer un boost::weak_ptr (voir boost documentation ). Un weak_ptr contient une référence à l'objet mais n'ajoute pas au nombre de références. L'objet est donc supprimé lorsqu'il n'existe que des références faibles.

4
Timbo

La base du partage est la confiance. Si certaines instances de votre programme doivent libérer le pointeur brut, il est presque certain que shared_ptr est du type incorrect. 

Cependant, récemment, je souhaitais faire de même, car je devais désallouer un processus différent. Finalement, on m'a appris que ma décision plus ancienne d'utiliser un std::shared_ptr n'était pas réfléchie. 

Je viens d'utiliser régulièrement ce type pour le nettoyage. Mais le pointeur était juste dupliqué à quelques endroits. En fait, j'avais besoin d'un std::unique_ptr, qui (surprise) a une fonction release

3
Andreas Spindler

Pardonnez-leur car ils ne savent pas ce qu’ils font . Cet exemple fonctionne avec boost :: shared_ptr et msvs std :: shared_ptr sans fuite de mémoire!

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;
}
2
Alexander Drichel

Je ne suis pas tout à fait sûr que votre question porte sur la réalisation de cet objectif, mais si vous voulez un comportement d'un shared_ptr, où, si vous relâchez la valeur d'un shared_ptr, tous les autres pointeurs partagés ayant la même valeur deviennent un nullptr, mettez un unique_ptr dans un shared_ptr pour obtenir ce comportement.

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

Sortie:

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

Ce comportement vous permet de partager une ressource (comme un tableau), puis de réutiliser ultérieurement cette ressource tout en invalidant toutes les références partagées avec cette ressource.

1
Aberrant

Vous pouvez supprimer le pointeur partagé, ce qui me semble être la même chose. Si les pointeurs sont toujours uniques, alors std::auto_ptr<> est un bon choix. N'oubliez pas que les pointeurs uniques ne peuvent pas être utilisés dans les conteneurs STL, car leurs opérations font beaucoup de copie et de duplication temporaire.

1
David Thornley

Si vos pointeurs sont vraiment uniques, utilisez std::unique_ptr ou boost::scoped_ptr si le premier n'est pas disponible pour votre compilateur. Sinon, envisagez de combiner l'utilisation de boost::shared_ptr avec boost::weak_ptr. Consultez la documentation Boost pour plus de détails.

0
Nicola Musatti

J'utilise Poco :: HTTPRequestHandlerFactory, qui s'attend à renvoyer un HTTPRequestHandler * brut, le framework Poco supprime le gestionnaire une fois la demande terminée. 

Utilisation également du projet DI Sauce pour créer les contrôleurs, cependant l'injecteur retourne shared_ptr que je ne peux pas retourner directement, et renvoyer handler.get () ne sert à rien non plus, car dès que cette fonction renvoie le shared_ptr sort de la portée et supprime puis handler avant son exécution, voici donc une raison raisonnable (je pense) d’avoir une méthode .release (). J'ai fini par créer une classe HTTPRequestHandlerWrapper comme suit: -

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);
    }
};

et puis l'usine serait

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);
}

qui satisfait à la fois Sauce et Poco et fonctionne bien.

0
steve

Voici un hack qui pourrait fonctionner. Je ne le recommanderais pas, sauf si vous êtes dans une situation difficile.

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();
}
0
Mark Ransom

J'avais besoin de passer un pointeur par le biais de gestionnaires asynchrones et de conserver le comportement d'autodestruction en cas d'échec, mais l'API finale attendait un pointeur brut.

#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;
}

Si ptr.use_count() != 1 vous obtiendrez une nullptr à la place.

0
Chnossos