删除矢量元素

时间:2012-03-23 07:56:56

标签: c++ memory vector

我有一个向量,用于存储类的所有对象(由new创建)的指针。 我为每个对象都有一个int成员变量(名为 id ),它存储了存储该对象地址的向量元素的索引。 此方法可以帮助我在 id 的帮助下调用任何对象。 id 在构造函数中借助于静态变量进行分配,每次创建对象时,该变量都会增加。 我的问题是,当我删除一个对象时,我无法删除向量元素(释放该元素占用的内存,因为我的向量是动态创建的)(因为如果我删除它,那么所有下一个元素的索引将减少1)。 所以我想知道一种方法,通过该方法我可以从向量中释放内存,但不会导致其他元素的索引发生变化。 请告诉我一个不耗费大量时间的方法。 (重新排列元素和然后更改每个对象的 id 等方法将耗费大量时间!) 我可能有大约1000个这样的对象,如果我删除第一个元素并且可能会消耗很多时间。如上所述做重排。 谢谢你

6 个答案:

答案 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。