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+⬑ * 默认成员函数 原来C++类中,有6个默认成员函数: 构造函数 析构函数 拷贝...
#pragma once // 1. 设计一个不能被拷贝的类/* 解析:拷贝只会放生在两个场景中:拷贝构造函数以及赋值运...
C类型转换 C语言:显式和隐式类型转换 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译...
//异常的概念/*抛出异常后必须要捕获,否则终止程序(到最外层后会交给main管理,main的行为就是终止) try...
#pragma once /*Smart pointer 智能指针;灵巧指针 智能指针三大件//1.RAII//2.像指针一样使用//3.拷贝问...
目录&lt;future&gt;future模板类成员函数:promise类promise的使用例程:packaged_task模板类例程...