2017-09-26 24 views
1

我有一个简单的对象缓存:如何避免常量-REF返回到一个临时的缓存

class ObjectCache 
{ 
public: 
    ObjectCache() {} 

    const Object& object(const std::string &key) const 
    { 
     auto it = cache_.find(key); 
     if (it != cache_.end()) 
     { 
      return it->second; 
     } 

     return Object(); // const-ref to temporary 
    } 

    void insert(const std::string &key, const Object &object) 
    { 
     cache_[key] = object; 
    } 

private: 
    std:map<std::string, Object> cache_; 
}; 

返回类型检索从缓存是一个const REF时。

但是,如果未找到该键,则会返回临时参数,并导致调用代码的未定义行为。

如何解决将const ref返回给临时的问题?

一些想法我有:

  • 插入并返回指针(缓存取得所有权),nullptr是指未发现
  • 提供ObjectCache ::包含了那么调用代码可以访问
  • 保持前检查无论是静态对象还是空对象成员,并在找不到时返回对该对象的引用
+0

你已经列出3个选项,他们有什么问题?它主要取决于调用代码应该是什么样子,以及每次返回默认对象时应该是一个新对象还是相同 – user463035818

+0

所有三个选项都是同样有效的IMO。第二个(带有'constains'函数)很容易进行线程数据争夺。 –

+1

第四种解决方案是使用'std :: optional'或'booost :: optiona'来包装引用。 – OutOfBound

回答

1

的理想解决方案是保持当前的高速缓存,但返回一个指针的引用:

class ObjectCache 
{ 
public: 
    ObjectCache() {} 

    const Object* object(const std::string &key) const 
    { 
     auto it = cache_.find(key); 
     if (it != cache_.end()) 
     { 
      return &(it->second); 
     } 

     return nullptr; 
    } 

    void insert(const std::string &key, const Object &object) 
    { 
     cache_[key] = object; 
    } 

private: 
    std:map<std::string, Object> cache_; 
}; 

这避免对堆和存储器管理创建对象的附加益处,但允许调用代码未找到与nullptr一起使用。

0

您可以提供一个允许调用者表示所需行为的接口:

#include <map> 
#include <string> 
#include <stdexcept> 
#include <boost/optional.hpp> 

struct Object {}; 

struct NoObject : std::logic_error 
{ 
    using std::logic_error::logic_error; 

}; 

class ObjectCache 
{ 
public: 
    ObjectCache() {} 

    /// Require an object for the corresponding key 
    /// @param key 
    /// @exception NoObject if the key does not represent an object 
    /// in cache 
    const Object& require(const std::string &key) const 
    { 
     auto it = cache_.find(key); 
     if (it != cache_.end()) 
     { 
      return it->second; 
     } 
     throw NoObject(key); 
    } 

    /// return the object corresponding to key if it exists 
    /// If not, call the factory function, store the result and return 
    /// the corresponding object 
    template<class Factory> 
    const Object& acquire(const std::string &key, Factory&& factory) 
    { 
     auto it = cache_.find(key); 
     if (it == cache_.end()) 
     { 
      it = cache_.emplace(key, factory(key)).first; 
     } 
     return it->second; 
    } 


    /// Return the object corresponding to key if it exists.  
    boost::optional<const Object&> query(const std::string &key) const 
    { 
     auto it = cache_.find(key); 
     if (it != cache_.end()) 
     { 
      return it->second; 
     } 
     else 
     { 
      return {}; 
     } 
    } 

    void insert(const std::string &key, const Object &object) 
    { 
     cache_[key] = object; 
    } 

private: 
    std::map<std::string, Object> cache_; 
}; 

int main() 
{ 
    ObjectCache cache; 

    // fetch existing or create 
    auto& x = cache.acquire("foo", [](std::string const& key){ 
     return Object(); 
    }); 

    // fetch existing or exception 
    auto& y = cache.require("foo"); 

    // fetch existing if exists 
    if(auto z = cache.query("foo")) 
    { 
     auto&& zz = z.get(); 
    } 

}