2017-09-26 46 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(); 
    } 

}