Comment supprimer des pointeurs intelligents (non intrusifs) d'un cache quand il n'y a plus de références?

en raison de ma réputation de noob, je ne peux pas répondre à cela Thread, en particulier la accepté de répondre:

je n'ai jamais utilisé boost::intrusives pointeurs intelligents, mais si vous utilisez shared_ptr pointeurs intelligents, vous pouvez utiliser weak objets pour votre cache.

ces pointeurs weak_ptr ne comptent pas comme référence lorsque le système décide de libérer leur mémoire, mais peuvent être utilisés pour récupérer un shared_ptr tant que l'objet n'a pas été supprimé encore.

c'est certainement une idée intuitive, cependant, la norme C++ ne supporte pas la comparaison de weak_ptrs, donc elle ne peut pas être utilisée comme clé pour les conteneurs associatifs. Ceci pourrait être contourné en implémentant un opérateur de comparaison pour weak_ptrs:

template<class Ty1, class Ty2>
    bool operator<(
        const weak_ptr<Ty1>& _Left,
        const weak_ptr<Ty2>& _Right
    );

Le problème avec cette solution est que

(1) l'opérateur de comparaison doit obtenir la propriété pour chaque comparaison (c.-à-d. créer shared_ptrs à partir de la weak_ptr refs)

(2) le weak_ptr n'est pas effacé du cache lorsque le dernier shared_ptr qui gère la ressource est détruit, mais un weak_ptr expiré est conservé dans le cache.

Pour (2), nous avons pu fournir une coutume destructeur ( DeleteThread), cependant, cela nécessiterait à nouveau de créer un weak_ptr à partir du T* qui est à supprimer, qui peut ensuite être utilisé pour effacer le weak_ptr du cache.

ma question serait s'il y a une meilleure approche à une cache utilisant des pointeurs intelligents (j'utilise le compilateur VC100, pas de boost), ou est-ce que je ne l'obtiens tout simplement pas?

Santé, Daniel

14
demandé sur Community 2011-12-09 17:47:14
la source

2 ответов

la chose est, votre Cache n'est pas abordé par l'objet lui-même, sinon il serait à peu près inutile.

L'idée d'un Cache est d'éviter un certain calcul, de sorte que l'index sera les paramètres du calcul, qui seront directement liés au résultat s'il est déjà présent.

Maintenant, vous pourriez avoir besoin d'un second index, pour supprimer des objets du cache, mais ce n'est pas obligatoire. Il y a certainement d'autres stratégies disponibles.

Si vous vous voulez vraiment retirer des objets du cache dès qu'ils ne sont pas utilisés ailleurs dans l'application, alors, effectivement, vous pouvez utiliser un index secondaire. L'idée ici sera d'indexer selon T*, pas weak_ptr<T>, mais weak_ptr<T> autour de, parce que sinon, vous ne pouvez pas créer un nouveau shared_ptr sur le même compte de référence.

La structure exacte dépend des paramètres du calcul sont difficiles à recalculer après les faits, s'ils le sont, un simple la solution est:

template <typename K, typename V>
class Cache: boost::enable_shared_from_this<Cache>
{
  typedef std::map<K, boost::weak_ptr<V>> KeyValueMap;
  typedef std::map<V*, KeyValueMap::iterator> DeleterMap;

  struct Deleter {
    Deleter(boost::weak_ptr<Cache> c): _cache(c) {}

    void operator()(V* v) {
      boost::shared_ptr<Cache> cache = _cache.lock();
      if (cache.get() == 0) { delete v; return; }

      DeleterMap::iterator it = _cache.delmap.find(v);
      _cache.key2val.erase(it->second);
      _delmap.erase(it);
      delete v;
    }

    boost::weak_ptr<Cache> _cache;
  }; // Deleter

public:
  size_t size() const { return _key2val.size(); }

  boost::shared_ptr<V> get(K const& k) const {
    KeyValueMap::const_iterator it = _key2val.find(k);
    if (it != _key2val.end()) { return boost::shared_ptr<V>(it->second); }

    // need to create it
    boost::shared_ptr<V> ptr(new_value(k),
        Deleter(boost::shared_from_this()));

    KeyValueMap::iterator kv = _key2val.insert(std::make_pair(k, ptr)).first;
    _delmap.insert(std::make_pair(ptr.get(), kv));

    return ptr;
  }


private:
  mutable KeyValueMap _key2val;
  mutable DeleterMap _delmap;
};

notez la difficulté particulière: le pointeur pourrait survivre à la Cache, donc nous avons besoin d'une astuce ici...

Et pour votre information, bien qu'il semble possible, je ne suis pas du tout à l'aise dans ce code: non testé, non prouvées, bla, bla ;)

2
répondu Matthieu M. 2011-12-19 23:42:20
la source

une solution possible pour ce que vous voulez accomplir pourrait être

Permet de dire T est votre objet et

  1. Seulement T* dans votre cache.
  2. avoir un deleter personnalisé pour votre shared_ptr<T>
  3. faites effacer votreT* à partir de la mémoire cache sur supprimer.

de cette façon le cache n'augmente pas le nombre de ref de votre shared_ptr<T> mais est notifié lorsque le nombre de réf. atteint 0.

struct Obj{};

struct Deleter
{
    std::set<Obj*>& mSet;
    Deleter( std::set<Obj*>& setIn  )
        : mSet(setIn) {}

    void operator()( Obj* pToDelete )
    {
        mSet.erase( pToDelete );
        delete pToDelete;
    }
};

int main ()
{

    std::set< Obj* > mySet;
    Deleter d(mySet);
    std::shared_ptr<Obj> obj1 = std::shared_ptr<Obj>( new Obj() , d );
    mySet.insert( obj1.get() );
    std::shared_ptr<Obj> obj2 = std::shared_ptr<Obj>( new Obj() , d );
    mySet.insert( obj2.get() );

    //Here set should have two elements
    obj1 = 0;
    //Here set will only have one element

    return 42;
}
4
répondu parapura rajkumar 2011-12-09 19:29:04
la source