我有一个地图,用于存储指向对象的指针。
typedef std::map<unsigned int, Entity*> entityMap;
entityMap entitymap;
要为实体分配ID,我可以在实体图中取最新值并将其增加1.
Entity *entity = new Entity;
entity->id = /*newest entity+1*/;
entitymap.insert(std::pair<unsigned int,Entity*>(entity->id,entity));
但是这个数字可能会变得不必要地大,因为有时会删除一个实体并将其从地图中移除。
std::map<unsigned int,Entity*>::iterator it;
it = entitymap.find(EntityID);
if(it != entitymap.end())
{
Entity *entity= it->second;
entitymap.erase(it);
}
delete entity;
所以我可以有一张包含这些值的地图;
1,2,4,8,10
在这种情况下,我希望下一个实体声明ID 3
。
答案 0 :(得分:4)
由于ID是按数字排序的,因此您可以浏览整个地图,直到找到“漏洞”:
unsigned int i = 1; // or whatever your smallest admissable key value is
for (auto it = m.cbegin(), end = m.cend();
it != end && i == it->first; ++it, ++i)
{ }
// now i is the next free index
如果地图很大并且第一个洞接近结尾,这可能需要很长时间。在开始此探索之前,您可以先检查最大键值(由m.crbegin()->first
给出)是否明显大于m.size()
。
答案 1 :(得分:1)
假设列表不会增长很多,您至少可以保留已发布的最小标识符。然后,当您重新使用它时,搜索Kerrek SB提到的下一个可用标识符。
class {
...
static int g_smallest_free_id; // init to 1
...
};
void delete_id()
{
if(m_id < g_smallest_free_id) {
m_id = g_smallest_free_id;
}
}
void new_id()
{
int id = g_smallest_free_id;
// the -1 is because it looks like you start your ids at 1
// since we skip all the known identifiers before id,
// the loop is reduced from the current id to the next only
for(interator it = list.begin() + id - 1;
it != list.end(); ++it) {
// find next available id
}
}
这是伪代码,向您显示最小的空闲标识符必须是您类中的静态变量(对所有实例都是通用的。)
如评论中所述,您可以使用向量代替。虽然它不会被排序,但您仍然无法无限期地增加标识符。矢量的唯一缺点就是你使用了一点内存......(如果你处理很多对象,那么很多,但地图也是如此。)
答案 2 :(得分:1)
您可以保留所有已释放密钥的Heap。每次释放密钥时都会将其添加到堆中,每次使用密钥时都会将其从堆中删除。这两个操作都是O(log n)。您将创建一个堆,以便根节点具有最小的密钥。
如果堆为空,那么您只需像往常一样递增前一个最大的密钥来分配一个新密钥。
答案 3 :(得分:1)
这可能很慢,具体取决于地图的密集程度,但相当容易理解:
typedef std::map<Key, Value> Table;
std::optional<Key> findFirstFreeSlot(const Key& start, const Key& end, const Table& table)
{
for (Key i = start; i <= end; i++) {
if (table.find(i) == table.end()) {
return i;
}
}
return std::none;
}