2012-04-05 144 views
2

目前我已經實現了地圖的,像這樣的值的引用計數緩存:C++:在std :: map中引用計數值;是std :: multimap更好的選擇?

//filename or name of bitmap, reference count, memory location... 
std::map<std::string, std::pair<long, BITMAP*> > _cache; 

使用的std :: multimap中更好的選擇?

//filename or name of bitmap, memory location... 
std::multimap<std::string, BITMAP*> _cache; 

或者只是一個不同的方式呢?

- 編輯 -

下面是我的意圖它的目的是成爲一個私有類,它是嚴格的實用工具,用戶不會看到代碼的其餘部分的清晰度的特定類。對他們來說,他們只是創造一個雪碧。 注意:BITMAP結構被認爲是私有的,創建/銷燬/修改一個結構的唯一方法是通過第三方C庫中的衆多功能之一,即使用原始指針的REQUIRE

BitmapCache.h

#ifndef A2DE_CBITMAPCACHE_H 
#define A2DE_CBITMAPCACHE_H 

#include "../a2de_vals.h" 
#include <allegro/file.h> 
#include <allegro/gfx.h> 
#include <allegro/draw.h> 
#include <allegro/datafile.h> 
#include <allegro/color.h> 

#include <map> 
#include <utility> 
#include <string> 

struct BITMAP; 

_A2DE_BEGIN 

class BitmapCache { 
public: 
    static BITMAP* GetBitmap(std::string filename); 
    static BITMAP* StoreBitmap(std::string name, BITMAP* bmp); 
    static BITMAP* RetrieveBitmap(std::string name); 
    static std::string GetBitmapName(BITMAP* file); 
    static void RemoveBitmap(std::string name); 

protected: 
private: 
    static std::map<std::string, std::pair<long, BITMAP*> > _cache; 
    static void CleanCache(); 

}; 

_A2DE_END 

#endif 

BitmapCache.cpp

#include "CBitmapCache.h" 

#include <algorithm> 
#include <map> 

_A2DE_BEGIN 

//filename or name of bitmap, reference count, memory location... 
typedef std::map<std::string, std::pair<long, BITMAP*> > MapStrBmp; 
typedef MapStrBmp::iterator MapStrBmpIter; 

MapStrBmp BitmapCache::_cache; 

BITMAP* BitmapCache::GetBitmap(std::string filename) { 
    //Return NULL if a bad filename was passed. 
    if(filename.empty()) return NULL; 
    if(exists(filename.c_str()) == false) return NULL; 

    //Reduce incorrect results by forcing slash equality. 
    filename = fix_filename_slashes(&filename[0]); 

    //Clean the cache if it's dirty. 
    CleanCache(); 

    //Search for requested BITMAP. 
    MapStrBmpIter _iter = _cache.find(filename); 

    //If found, return it. 
    if(_iter != _cache.end()) { 
     _iter->second.first++; 
     return _iter->second.second; 
    } 

    //Otherwise, create it, store it, then return it. 
    BITMAP* result = load_bmp(filename.c_str(), NULL); 
    if(result == NULL) return NULL; 
    _cache.insert(std::make_pair(filename, std::make_pair(static_cast<long>(1), result))); 
    return result; 
} 

BITMAP* BitmapCache::StoreBitmap(std::string name, BITMAP* bmp) { 
    if(name.empty() || bmp == NULL) return NULL; 

    CleanCache(); 
    name = fix_filename_slashes(&name[0]); 
    MapStrBmpIter _iter = _cache.find(name); 
    if(_iter != _cache.end()) { 
     _iter->second.first++; 
     return _iter->second.second; 
    } 

    _cache.insert(std::make_pair(name, std::make_pair(static_cast<long>(1), bmp))); 
    return bmp; 
} 
BITMAP* BitmapCache::RetrieveBitmap(std::string name) { 
    if(name.empty()) return NULL; 

    name = fix_filename_slashes(&name[0]); 
    MapStrBmpIter _iter = _cache.find(name); 
    if(_iter != _cache.end()) { 
     _iter->second.first++; 
     return _iter->second.second; 
    } 
    return NULL; 
} 

void BitmapCache::RemoveBitmap(std::string name) { 
    if(name.empty()) return; 

    name = fix_filename_slashes(&name[0]); 
    MapStrBmpIter _iter = _cache.find(name); 

    if(_iter != _cache.end()) { 
     _iter->second.first--; 
     CleanCache(); 
    } 
} 

std::string BitmapCache::GetBitmapName(BITMAP* file) { 
    if(file == NULL) return std::string(""); 

    CleanCache(); 
    MapStrBmpIter b = _cache.begin(); 
    MapStrBmpIter e = _cache.end(); 
    for(MapStrBmpIter _iter = b; _iter != e; ++_iter) { 
     if(_iter->second.second != file) continue; 
     return _iter->first; 
    } 
    return std::string(""); 
} 

void BitmapCache::CleanCache() { 

    //Clean the cache of any bitmaps that are no longer referenced. 
    MapStrBmpIter b = _cache.begin(); 
    MapStrBmpIter e = _cache.end(); 
    for(MapStrBmpIter _iter = b; _iter != e; /* DO NOTHING */) { 
     if(_iter->second.first > 0) { 
      ++_iter; 
      continue; 
     } 
     destroy_bitmap(_iter->second.second); 
     _iter->second.second = NULL; 
     _cache.erase(_iter++); 
    } 
} 

_A2DE_END 
+4

'std :: shared_ptr'。 – 2012-04-05 04:06:27

回答

0

嗯,事實證明我不能使用STL智能指針與我想要做的。重新運行上述代碼以使用智能指針後,程序一直處於崩潰狀態。事實證明,allegro 4.2庫API明確指出,在allegro被初始化之後嘗試調用allegro方法(特別是處理位圖的方法)會導致程序崩潰。智能指針在程序結束並且快捷程序被初始化之後纔會銷燬自己(並試圖調用它們包含的指針的刪除方法),從而導致程序崩潰。

所以目前,我自己實現引用計數的當前解決方案是解決方法。

感謝您的建議和幫助。

4
std::map<std::string, std::pair<long, BITMAP*> > _cache; 

不要重複發明輪子。使用shared_ptr(可在boost或tr1命名空間中的某些編譯器中使用,或在較新編譯器中的std :: namespace中使用)或任何其他現有經過良好測試的智能指針類。重新發明輪子是常見的編程錯誤之一 - 通過嘗試重新實現某些東西(已經由其他人寫入),您將浪費開發時間並且一無所獲。

- 編輯 -

和destroy_bitmap方法

的boost :: shared_ptr的支持自定義刪除器。使用它們。

我會怎麼做時,相同的文件名中

std::map<std::string, boost::weak_ptr<BITMAP> >傳遞不會產生這麼幾個位圖。如果map中不存在值,或者現有的weak_ptr已過期,請使用deleter創建新的shared_ptr,並將weak_ptr放入map並返回此shared_ptr。否則(weak_ptr未過期)從weak_ptr中提取shared_ptr並返回它。

當然,這取決於使用模式。如果您沒有編寫某種「資源緩存/池」(即使它們位於地圖內,也會刪除未使用的資源),那麼可以在地圖內使用shared_ptr和刪除器。

+0

BITMAP *是我只能公開訪問的數據結構。它有自己的'create_bitmap'(返回一個指向BITMAP的指針或通過獲取文件名的NULL)和'destroy_bitmap'方法(帶一個指向BITMAP的指針)。如果我按照您的建議將實現更改爲'std :: shared_ptr',那麼我該怎麼做,以便在傳入相同文件名時不創建多個BITMAP,也不會從一種方法複製到另一種方法? – Casey 2012-04-05 04:53:48

+0

@Casey你不需要改變任何東西。 'std :: shared_ptr p(create_bitmap(/ *無論是否需要* /),&destroy_bitmap);'開箱即用,做正確的事情。然後把它放在'std :: map >'。 (然後編寫一個接口,在查找失敗時創建適當的「BITMAP」。) – 2012-04-05 05:29:02

+0

@Casey:更新了答案。 – SigTerm 2012-04-05 05:32:57

0

假設你有:

struct BITMAP; 
BITMAP* create_bitmap(std::string const& filename); 
void destroy_bitmap(BITMAP*); 

那麼你可以有:

typedef std::shared_ptr<BITMAP> bitmap_ptr; 

class bitmap_cache { 
public: 
    bitmap_ptr 
    make(std::string const& filename) 
    { 
     auto it = map.find(filename); 
     if(it != map.end()) { 
      return it->second; 
     } else { 
      bitmap_ptr p(create_bitmap(filename), &destroy_bitmap); 
      map.insert(std::make_pair(filename, p)); 
      return p; 
     } 
    } 

private: 
    std::map<std::string, bitmap_ptr> map; 
}; 

注意,每個bitmap_cache維持所有創建BITMAP還活着的他們的一生中,但未使用BITMAP旨意正常是在高速緩存到達其使用期限結束時處理(由高速緩存的某些客戶端使用的s將保持安全存活狀態)。如果你需要的話,你可以在地圖上使用std::weak_ptr<BITMAP>來改善。

+0

不會返回'shared_ptr'暴露內部工作?用戶如何才能得到一個原始的'BITMAP *'(根據所使用的第三方C庫的其他功能需要...),同時仍然根據需要維護引用計數? – Casey 2012-04-05 12:31:07

+0

@Casey「std :: shared_ptr」的全部內容是將「BITMAP」與引用計數打包在一起。用戶可以做的唯一有風險的事情是用'BITMAP *'提前調用'destroy_bitmap' - 但只要用戶可以訪問該'BITMAP *',*總是*可行的。所以這是不值得的。 – 2012-04-05 12:55:43