如果没有更多引用,如何从缓存中删除(非侵入式)智能指针?

时间:2011-12-09 13:47:15

标签: c++ caching shared-ptr smart-pointers weak-ptr

由于我的诺布声誉,我无法回复此Thread,特别是接受的答案:

  

我从未使用过boost :: intrusive智能指针,但如果您使用shared_ptr智能指针,则可以使用weak_ptr对象作为缓存。

     

当系统决定释放内存时,那些weak_ptr指针不算作引用,但只要该对象尚未被删除,它就可用于检索shared_ptr。

这当然是一个直观的想法,但是,C ++标准不支持weak_ptrs的比较,因此它不能用作关联容器的键。这可以通过为weak_ptrs实现比较运算符来规避:

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

此解决方案的问题在于

(1)比较运算符必须获得每次比较的所有权(即从weak_ptr refs创建shared_ptrs)

(2)当管理资源的最后一个shared_ptr被破坏时,weak_ptr不会从缓存中删除,但过期的weak_ptr会保留在缓存中。

对于(2),我们可以提供一个自定义析构函数(DeleteThread),但是,这将需要再次从要删除的T *创建一个weak_ptr,然后可以用来擦除weak_ptr来自缓存。

我的问题是,如果有更好的方法使用智能指针缓存(我使用VC100编译器,没有提升),或者我根本得不到它?

干杯,丹尼尔

2 个答案:

答案 0 :(得分:4)

您希望实现的目标可能是

让我们说T是你的对象,shared_ptr<T>是你的共享ptr

  1. 只在缓存中定期T*
  2. 为您的shared_ptr<T>
  3. 设置自定义删除器
  4. 让您的自定义删除程序在删除后从缓存中删除T*
  5. 这样,缓存不会增加shared_ptr<T>的引用计数,但会在引用计数达到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;
    }
    

答案 1 :(得分:2)

问题是,你的Cache没有被缓存的对象解决,否则就会无用。

Cache的想法是避免一些计算,因此索引将是计算的参数,如果已经存在,它将直接映射到结果。

现在,您可能确实需要第二个索引来从缓存中删除对象,但这不是必需的。当然还有其他策略可供选择。

如果您确实希望在应用程序中的任何其他位置不使用对象时立即从缓存中删除对象,那么,实际上,您可以使用辅助索引。这里的想法虽然是根据T*而不是weak_ptr<T>进行索引,但要保持weak_ptr<T>,因为否则您无法在相同的引用计数上创建新的shared_ptr

确切的结构取决于计算的参数是否很难在事后重新计算,如果是,一个简单的解决方案是:

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

注意特殊困难:指针可能比Cache更长,所以我们需要一些技巧......

对于您的信息,虽然看起来可行,但我对此代码完全没有信心:未经测试,未经证实,bla,bla;)