我有一个向量,用于存储类的所有对象(由new
创建)的指针。
我为每个对象都有一个int成员变量(名为 id ),它存储了存储该对象地址的向量元素的索引。
此方法可以帮助我在 id 的帮助下调用任何对象。
id 在构造函数中借助于静态变量进行分配,每次创建对象时,该变量都会增加。
我的问题是,当我删除一个对象时,我无法删除向量元素(释放该元素占用的内存,因为我的向量是动态创建的)(因为如果我删除它,那么所有下一个元素的索引将减少1)。
所以我想知道一种方法,通过该方法我可以从向量中释放内存,但不会导致其他元素的索引发生变化。
请告诉我一个不耗费大量时间的方法。 (重新排列元素和然后更改每个对象的 id 等方法将耗费大量时间!)
我可能有大约1000个这样的对象,如果我删除第一个元素并且可能会消耗很多时间。如上所述做重排。
谢谢你
答案 0 :(得分:4)
您只需要将一个元素重新排列:swap
向量的最后一个元素到要删除的元素的位置,然后删除最后一个元素。
int to_delete = …;
swap(my_vec[to_delete], my_vec[my_vec.end() - 1]);
my_vec[to_delete].index = to_delete;
delete my_vec.back();
答案 1 :(得分:2)
你的设计很不寻常。如果您希望保持对象的位置,使用向量不是您的最佳选择。您可以将对象存储在列表,集合或映射中,这些对象在删除对象时都会保留对象的位置。这样你就可以通过指针而不是id来访问对象(尽管使用map你可以使用两者)
如果由于某种原因绝对必须使用向量,可以使用“swap and pop”技巧删除对象。删除对象时,将其与向量中的最后一个元素交换,然后调用pop_back()以删除最后一个元素。然后,您需要使用它的新ID更新刚刚移动的元素。但是这个操作是在恒定的时间内运行的。
答案 2 :(得分:1)
有第二个可重复使用的插槽/ ID列表,每次新建元素时都会先检查它们。删除元素时,将元素的id添加到可重用的ID列表中:
//Somewhere in the code
vector<Object*> s_objects;
vector<int> s_freeIds;
//On new:
if (s_freeIds.empty())
{
s_objects.push_back(new Object());
s_objects.back().id = s_objects.size() - 1;
}
else
{
int id = s_freeIds.back();
s_freeIds.pop_back();
s_objects[id] = new Object();
s_objects[id].id = id;
}
//On delete
s_freeIds.push_back(this->id);
答案 3 :(得分:1)
您可能需要考虑使用map<int,your_object_type*>
而不是vector<your_object_type*>
。然后,您不需要'重新索引'或(更重要的是,我认为)在删除项目时重新分配ID。
答案 4 :(得分:0)
最简单的解决方案可能是不从向量中删除元素
完全,但只需用空指针替换它。这确实意味着
当遍历整个向量时,您必须检查null
指针,但这通常不是一个大问题。并防止
无限增长的矢量,你必须能够重用插槽;
要么扫描向量以获取空指针(std::find
)
插入,或者您维护某种空闲插槽列表。
请注意,如果您的id要用作向量的索引,那么您
不想独立生成它。如果使用插入
push_back
(因为向量中没有空指针),id
v.size() - 1
之后是push_back
,或之前只是v.size()
;如果
您使用返回的迭代器插入特定位置
std::find
,然后标识符是迭代器 - v.begin()
。如果
你只是使用线性搜索(并且使用小到1000的向量)
元素,这很可能),然后像下面这样的东西
应该工作:
// returns index of inserted element
int
insertIntoVector( std::vector<MyType*>& index, MyType* newObject )
{
std::vector<MyType*>::iterator position
= std::find( index.begin(), index.end(), NULL );
int results = position - index.begin();
if ( position == index.end() ) {
index.push_back( newObject );
} else {
*position = newObject;
}
return results;
}
如果您要缓存空闲广告位,请将std::find
替换为
找到空闲插槽的相应代码。
答案 5 :(得分:0)
真正的问题:你为什么使用vector
?
首先,关于生成ID:
typedef size_t ID;
static ID& accessID() { static ID id = 0; return id; }
ID currentID() { return accessID(); }
ID newID() { return ++accessID(); }
其次,这是一个典型的工厂情况,所以让我们提供Factory
实施。
class ObjectFactory {
typedef std::unordered_map<ID, Object> Register;
public:
Object& create() {
ID const id = newID();
return register.insert(std::make_pair(id, Object(id))).first->second;
}
Object* access(ID id) {
Register::iterator it = register.find(id);
return it == register.end() ? nullptr : &it->second;
}
Object const* get(ID id) const {
Register::const_iterator it = register.find(id);
return it == register.end() ? nullptr : &it->second;
}
bool remove(ID id) {
return register.erase(id);
}
private:
Register register;
};
您可以选择将当前ID作为工厂的成员,或者将它保持分开,以防您想要不同的工厂并且害怕混淆ID。