2

目前我已经实现了映射值的引用计数缓存,如下所示:

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

或者只是一种不同的方式?

- 编辑 -

这是为了清楚我的意图的特定类它旨在成为一个私有类,对用户从未见过的其余代码严格实用。对他们来说,他们只是在创建一个 Sprite。注意:BITMAP结构被认为是私有的,创建/销毁/修改结构的唯一方法是通过第 3 方 C 库中需要使用原始指针的众多函数之一

位图缓存.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

位图缓存.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

3 回答 3

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

不要重新发明轮子。使用shared_ptr(在某些编译器的 boost 或 tr1 命名空间中或在较新编译器的 std:: 命名空间中可用)或任何其他现有的经过良好测试的智能指针类。重新发明轮子是常见的编程错误之一 - 通过尝试重新实现某些东西(已经由其他人编写),您将浪费开发时间而一无所获。

- 编辑 -

和 destroy_bitmap 方法

boost::shared_ptr 支持自定义删除器。使用它们。

我该怎么做才能在传入相同的文件名时不创建几个 BITMAP

std::map<std::string, boost::weak_ptr<BITMAP> >. 如果 map 中不存在 value,或者现有的 weak_ptr 已过期,则使用 deleter 创建新的 shared_ptr 并将 weak_ptr 放入 map 并返回此 shared_ptr。否则(weak_ptr 尚未过期)从 weak_ptr 中提取 shared_ptr 并返回它。

当然,这取决于使用模式。如果您没有编写某种“资源缓存/池”(未使用的资源即使在地图中也会被删除),那么您可以shared_ptr在地图中使用删除器。

于 2012-04-05T04:10:48.473 回答
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的 s 在其生命周期内都保持活动状态,但未使用BITMAP的 s 将在缓存到达其生命周期结束时被正确处理(缓存BITMAP的某些客户端正在使用的 s 将保持安全活动状态)。如果需要,您可以通过std::weak_ptr<BITMAP>在地图中使用来改进这一点。

于 2012-04-05T05:40:04.070 回答
0

好吧,事实证明我不能将 STL 智能指针用于我正在尝试做的事情。在重新处理上述代码以使用智能指针后,程序在关闭时不断崩溃。事实证明,allegro 4.2 库 API 明确指出,在取消初始化 allegro 后尝试调用 allegro 方法(特别是处理位图的方法......)会使程序崩溃。智能指针只有在超出范围后才会销毁自己(并尝试调用其包含的指针的删除器方法)......在程序结束并且 allegro 被取消初始化之后,这反过来又使程序崩溃。

所以现在,滚动我自己的引用计数实现的当前解决方案是变通方法。

无论如何,感谢您的建议和帮助。

于 2012-04-10T22:52:32.627 回答