2016-10-31 4 views
3

Nous utilisons la composition, quand un objet a un seul parent, ce qui devrait prendre en compte la durée de vie de l'objet. Nous utilisons unique_ptr dans la même situation, mais l'objet peut être nullptr.Comment gérer la durée de vie du type "le plus court de"?

Nous utilisons shared_ptr lorsque plusieurs entités externes peuvent avoir besoin de notre objet, donc sa durée de vie est prolongée jusqu'à ce que la dernière de ces entités externes perd son intérêt.

Ici, je veux poser une question sur une autre situation de vie. Que faire si l'objet doit vivre le plus court de plusieurs durées?

Voici un exemple. Prenons une minuterie à un coup, qui stocke un foncteur et l'exécute une fois le comptage terminé. Il est logique de moi *, que cet objet de la minuterie est détruite après:

1. fulfilling its task - therefore it should be able to destroy istelf 

or 

2. the parent loosing interest in the timer - so the parent should be able to 
    destroy the object as well 

Actuellement, je l'aide d'une mise en œuvre maladroite avec des pointeurs uniques. Quel serait un bon modèle/directive/mise en œuvre de ce problème?

raisons *: 1) le foncteur pourrait être propriétaire d'autres ressources 2) la minuterie aurait pu être mis à un très grand nombre, puis abandonnés 3) si le parent a été détruit, nous ne généralement » Je ne veux pas invoquer ses callbacks

+0

Cela a beaucoup en commun avec l'annulation de thread, lorsque vous calculez quelque chose en réponse à une requête réseau, mais la connexion a été interrompue brusquement. Compléter l'informatique ne causerait aucune corruption mais serait un gaspillage de ressources. – curiousguy

Répondre

0

Je pense que la meilleure solution est d'avoir un objet wrapper avec un pointeur unique vers l'objet timer actuel, et un getter pour l'objet timer qui retourne null s'il a été détruit. De cette façon, si le temporisateur expire et est détruit, l'appelant qui n'a pas perdu intérêt ne détient pas un pointeur apparemment valide mais en réalité invalide sur l'objet temporisateur.

2

Il existe de sérieux problèmes de concurrence et de réentrance.

Lorsque deux bits ou plus de code ont le droit de supprimer un pointeur, aucun morceau de code ne peut déréférencer de manière fiable ce pointeur, comme le fait l'autre pourrait le détruire. De même, toute branche où vous vérifiez que vous possédez la propriété peut devenir obsolète au moment où tout autre appel non local (par exemple un appel de fonction) s'exécute, même sans concurrence.

Nous pouvons contourner ce problème.

template<class T> 
struct shared_destroyable { 
    std::shared_ptr<T> lock() const { 
    return atomic_load<T>(ptr.get()); 
    } 
    explicit operator bool() const { return (bool)lock; } 
    void reset(std::shared_ptr<T> pin = {}) { 
    atomic_store(ptr.get(), std::move(pin)); 
    } 
    shared_destroyable(std::shared_ptr<T> pin): 
    ptr(std::make_shared<std::shared_ptr<T>>(std::move(pin)) 
    {} 
    shared_destroyable()=default; 
    shared_destroyable(shared_destroyable&&)=default; 
    shared_destroyable(shared_destroyable const&)=default; 
    shared_destroyable& operator=(shared_destroyable&&)=default; 
    shared_destroyable& operator=(shared_destroyable const&)=default; 
private: 
    std::shared_ptr<std::shared_ptr<T>> ptr; 
}; 

Cette vague se comporte comme un weak_ptrshared_ptr hybride.

Si le dernier s'en va, l'objet est détruit.

Cependant, si l'un d'eux .reset(), l'objet est détruit dès que le dernier autre code qui a lock() ed a mis fin à son champ d'application.

Changements d'affectation auxquels il est fait référence.

donc à l'utilisation que vous faites ceci:

if(auto sp = sd.lock()) { 
    // use sp 
} 

et la durée de vie sp est garanti pour durer toute la portée de la {}, même si quelqu'un fait un sd.reset() dans le bloc, dans un autre thread, ou lorsque vous appelez une autre méthode.