c – 使用索引避免迭代器无效,维护干净的界面

我已经创建了一个MemoryManager< T>这个类基本上是围绕两个向量的指针来包装的,这些指针管理堆分配对象的生命周期.

一个向量存储“活着”对象,另一个向量存储将被添加到下一个MemoryManager< T> ::刷新的对象.

选择该设计以避免在循环遍历MemoryManager T时的迭代器无效,因为直接将新对象直接添加到MemoryManager< T> :: alive向量可以使现有迭代器(如果其大小增加)失效.

template<typename T> struct MemoryManager {
    std::vector<std::unique_ptr<T>> alive;
    std::vector<T*> toAdd;

    T& create() { 
        auto r(new T); 
        toAdd.push_back(r); 
        return *r; 
    }

    T& refresh() { 
         // Use erase-remove idiom on dead objects
         eraseRemoveIf(alive,[](const std::unique_ptr<T>& p){ return p->alive; });

         // Add all "toAdd" objects and clear the "toAdd" vector
         for(auto i : toAdd) alive.emplace_back(i); 
         toAdd.clear(); 
    }  

    void kill(T& mItem)  { mItem.alive = false; }

    IteratorType begin() { return alive.begin(); }
    IteratorType end()   { return alive.end(); }
}

我在游戏引擎中使用它来存储实体,并且每帧更新每个“活着”实体:

void game() {
    MemoryManager<Entity> mm;

    while(gameLoop) {
        mm.refresh();
        for(auto e : mm) processEntity(e);
        auto& newEntity = mm.create();
        // do something with newEntity
    }
}

这让我经常创建/杀死实体,而不用担心他们的一生的太多.

不过,我最近得出的结论是,使用两个std :: vector是不必要的.我可以简单地使用单个向量并将迭代器存储到“最后一个活动对象”中,在上述迭代器之后立即添加新创建的对象:

在我看来,这个想法很好,但实际上我不能使用迭代器类型作为结束(如图所示),因为它可能会在向量中添加一些新元素后失效.我已经测试过,这种情况经常发生,导致崩溃.

我可以想到的另一个解决方案是使用索引而不是迭代器.这将解决崩溃,但是由于MemoryManager> :: begin和MemoryManager< T> :: end需要返回一个迭代器,所以我不能使用酷的C 11 for(x:y)foreach循环.

有没有办法用单个向量来实现当前的行为,并且仍然保持一个清晰的界面,可以用于C 11 for-each循环?

解决方法

您可以实现自己的迭代器类.

以下可能有帮助.

template <typename T,typename... Ts>
class IndexIterator : public std::iterator<std::random_access_iterator_tag,T>
{
public:
    IndexIterator(std::vector<T,Ts...>& v,std::size_t index) : v(&v),index(index) {}

    // if needed.
    typename std::vector<T,Ts...>::iterator getRegularIterator() const { return v->begin() + index; }

    T& operator *() const { return v->at(index); }
    T* operator ->() const { return &v->at(index); }

    IndexIterator& operator ++() { ++index; return *this;}
    IndexIterator& operator ++(int) { IndexIterator old(*this); ++*this; return old;}
    IndexIterator& operator +=(std::ptrdiff_t offset) { index += offset; return *this;}
    IndexIterator operator +(std::ptrdiff_t offset) const { IndexIterator res (*this); res += offset; return res;}

    IndexIterator& operator --() { --index; return *this;}
    IndexIterator& operator --(int) { IndexIterator old(*this); --*this; return old;}
    IndexIterator& operator -=(std::ptrdiff_t offset) { index -= offset; return *this;}
    IndexIterator operator -(std::ptrdiff_t offset) const { IndexIterator res (*this); res -= offset; return res;}

    std::ptrdiff_t operator -(const IndexIterator& rhs) const { assert(v == rhs.v); return index - rhs.index; }

    bool operator == (const IndexIterator& rhs) const { assert(v == rhs.v); return index == rhs.index; }
    bool operator != (const IndexIterator& rhs) const { return !(*this == rhs); }

private:
    std::vector<T,Ts...>* v;
    std::size_t index;
};

template <typename T,typename... Ts>
IndexIterator<T,Ts...> IndexIteratorBegin(std::vector<T,Ts...>& v)
{
    return IndexIterator<T,Ts...>(v,0);
}

template <typename T,Ts...> IndexIteratorEnd(std::vector<T,v.size());
}

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...