2012-04-05 68 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