2013-03-02 99 views
2

我正在设计一个网格的容器,表示为一维数组(模板)。我在这里发布代码的摘录,其实还有更多。它被用作机器人应用中的滚动占用网格,其中每个单元代表世界的一个小区域。自定义容器的自定义迭代器

一次操作我与电网经常做的是通过所有的细胞,并取回他们的世界坐标:

for(unsigned r=0; r<mygrid.rows_; ++r) { 
    for(unsigned c=0; c<mygrid.cols_; ++c) { 
    cell = mygrid.getRC(r,c); 
    mygrid.rcToXY(r,c,&x,&y); 
    } 
} 

我想有一个迭代器比存储所有的这些:电池,其rc坐标及其xy坐标。

for(Grid<CellType>::const_iterator it=mygrid.begin(); it!=mygrid.end(); ++it) { 
    cell = *it; 
    printf("%d %d %f %f\n", it.r(), it.c(), it.x(), it.y()); 
} 

在网上有很多答案和教程,我想出了以下实现,它的工作原理。然而,对我来说这似乎有点笨拙,为了学术的缘故,我想让它看起来更好。 STL兼容性也很好。

template <class G, typename C> 
class base_iterator 
{ 
private: 
    G* grid_; 
    C* cell_; 
    unsigned r_, c_; // local 
    double x_, y_; 

// this should be private with access for friends (Grid) only 
// but I can't make it work 
public: 
    base_iterator(G* grid, unsigned r, unsigned c) : grid_(grid), r_(r), c_(c) 
    { 
    cell_ = (r<grid->rows_ && c<grid->cols_) ? &grid_->getRC(r,c) : 0; 
    grid_->rcToXY(r,c,&x_,&y_); 
    } 


public: 
    base_iterator() : grid_(0) { } 

    // used to cast an iterator to a const_iterator 
    template <class G2, typename C2> 
    base_iterator(const base_iterator<G2,C2>& other) 
    { 
    grid_ = other.grid(); 
    cell_ = & other.cell(); 
    r_ = other.r(); 
    c_ = other.c(); 
    x_ = other.x(); 
    y_ = other.y(); 
    } 

    // this should be private with access for friends only 
    G* grid() const { return grid_; } 

    C& cell() { return *cell_; } 
    const C& cell() const { return *cell_; } 
    unsigned r() const { return r_; } 
    unsigned c() const { return c_; } 
    double x() const { return x_; } 
    double y() const { return y_; } 

    C* operator->() { return cell_; } 
    const C* operator->() const { return cell_; } 

    C& operator*() { return *cell_; } 
    const C& operator*() const { return *cell_; } 

    //prefix 
    base_iterator & operator++() 
    { 
    // my iteration logic here which needs access to grid 
    // in order to find the number of rows, etc. 
    return *this; 
    } 

    //postfix 
    base_iterator operator++(int) 
    { 
    base_iterator it(*this); // make a copy for result 
    ++(*this);    // Now use the prefix version to do the work 
    return it;   // return the copy (the old) value. 
    } 

    template <class G2, typename C2> 
    bool operator==(const base_iterator<G2,C2> & other) const 
    { 
    return cell_ == &other.cell(); 
    } 

    template <class G2, typename C2> 
    bool operator!=(const base_iterator<G2,C2>& other) const 
    { return cell_!=other.cell(); } 
}; 

然后在我的网格类:

typedef base_iterator<Grid<T>,T> iterator; 
    typedef base_iterator<Grid<T> const, T const> const_iterator; 

    iterator begin() { return iterator(this,0,0); } 
    iterator end() { return iterator(this,rows_,cols_); } 

    const_iterator begin() const { return const_iterator(this,0,0); } 
    const_iterator end() const { return const_iterator(this,rows_,cols_); } 

再次,这工作,但我觉得这是一个有点笨拙(见迭代器代码中的注释),我想知道如何改善它。我看到很多关于使用boost迭代器外观或适配器的文章,但我无法弄清楚如何使其适应我的情况。

回答

1

那么我找到了一个解决方案,我很满意。以下是完整的清单供参考。有一些棘手的部分花了我一些时间,特别是为了能够在类声明之外进行实现。根据我在这里和这里阅读的内容,我还没有设法让base_iterator类成为非嵌套的Grid类,我认为这是不可能的。

#include <cstdio> 
#include <cassert> 
#include <stdexcept> 
#include <algorithm> 


template <class T> 
class Grid 
{ 
public: 
    // these should be private, with public getters... 
    double resolution_; 
    unsigned rows_, cols_; 
    int map_r0_, map_c0_; // grid coordinates of origin of map 

private: 
    T* cell_; 

public: 
    Grid(double resolution, unsigned rows, unsigned cols); 
    ~Grid() { delete[] cell_; } 

    T & getRC(unsigned r, unsigned c); 
    const T & getRC(unsigned r, unsigned c) const; 
    void rcToXY(unsigned r, unsigned c, double* x, double* y) const; 

public: 
    template <class GridType, class CellType> 
    class base_iterator : std::iterator<std::forward_iterator_tag, CellType> 
    { 
    private: 
    friend class Grid; 
    GridType* grid_; 
    CellType* cell_; 
    unsigned r_, c_; // local 
    double x_, y_; 

    base_iterator(GridType* grid, unsigned r, unsigned c); 

    public: 
    base_iterator() : grid_(0) { } 

    template <class G2, class C2> 
    base_iterator(const base_iterator<G2,C2>& other); 

    CellType& cell() { return *cell_; } 
    const CellType& cell() const { return *cell_; } 
    unsigned r() const { return r_; } 
    unsigned c() const { return c_; } 
    double x() const { return x_; } 
    double y() const { return y_; } 

    CellType* operator->() { return cell_; } 
    const CellType* operator->() const { return cell_; } 

    CellType& operator*() { return *cell_; } 
    const CellType& operator*() const { return *cell_; } 

    //prefix 
    base_iterator & operator++(); 

    //postfix 
    base_iterator operator++(int); 

    template <class G2, class C2> 
    bool operator==(const base_iterator<G2,C2> & other) const 
    { return cell_ == other.cell_; } 

    template <class G2, class C2> 
    bool operator!=(const base_iterator<G2,C2>& other) const 
    { return cell_ != other.cell_; } 
    }; 

    typedef base_iterator<Grid<T>,T> iterator; 
    typedef base_iterator<Grid<T> const, T const> const_iterator; 

    iterator begin() { return iterator(this,0,0); } 
    iterator end() { return iterator(this,rows_,0); } 

    const_iterator begin() const { return const_iterator(this,0,0); } 
    const_iterator end() const { return const_iterator(this,rows_,0); } 
}; 


template <class T> 
Grid<T>::Grid(double resolution, unsigned rows, unsigned cols) 
    : resolution_(resolution), rows_(rows), cols_(cols), map_r0_(0), map_c0_(0) 
{ 
    cell_ = new T[rows_*cols_]; 
} 

template <class T> 
T & Grid<T>::getRC(unsigned r, unsigned c) 
{ 
    if (r >= rows_ || c >= cols_) 
    throw std::runtime_error("Out of bounds"); 
    return cell_[r * cols_ + c]; 
} 

template <class T> 
const T & Grid<T>::getRC(unsigned r, unsigned c) const 
{ 
    if (r >= rows_ || c >= cols_) 
    throw std::runtime_error("Out of bounds"); 
    return cell_[r * cols_ + c]; 
} 

template <class T> 
void Grid<T>::rcToXY(unsigned r, unsigned c, double* x, double* y) const 
{ 
    *x = (map_c0_ + c + 0.5) * resolution_; 
    *y = (map_r0_ + r + 0.5) * resolution_; 
} 


template <class T> 
template <class GridType, class CellType> 
Grid<T>::base_iterator<GridType,CellType>::base_iterator(GridType* grid, unsigned r, unsigned c) 
: grid_(grid), r_(r), c_(c) 
{ 
    if(r<grid->rows_ && c<grid->cols_) { 
    cell_ = &grid_->getRC(r,c); 
    grid_->rcToXY(r,c,&x_,&y_); 
    } 
    else 
    cell_ = &grid_->getRC(grid->rows_-1,grid->cols_-1) + 1; 
} 

// beautiful triple template declaration ! 
template <class T> 
template <class GridType, class CellType> 
template <class G2, class C2> 
Grid<T>::base_iterator<GridType,CellType>::base_iterator(const Grid<T>::base_iterator<G2,C2>& other) 
{ 
    grid_ = other.grid_; 
    cell_ = other.cell_; 
    r_ = other.r(); 
    c_ = other.c(); 
    x_ = other.x(); 
    y_ = other.y(); 
} 

template <class T> 
template <class GridType, class CellType> 
Grid<T>::base_iterator<GridType,CellType> & Grid<T>::base_iterator<GridType,CellType>::operator++() 
{ 
    assert(grid_!=0); 

    if(c_==grid_->cols_-1) 
    { 
    c_ = 0; 
    x_ = (grid_->map_c0_ + 0.5) * grid_->resolution_; 
    ++r_; 
    y_ += grid_->resolution_; 
    } 
    else 
    { 
    ++c_; 
    x_ += grid_->resolution_; 
    } 
    ++cell_; 

    return *this; 
} 

template <class T> 
template <class GridType, class CellType> 
Grid<T>::base_iterator<GridType,CellType> Grid<T>::base_iterator<GridType,CellType>::operator++(int) 
{ 
    base_iterator it(*this); // make a copy for result 
    ++(*this);    // Now use the prefix version to do the work 
    return it;   // return the copy (the old) value. 
} 

void print(unsigned i) 
{ 
    printf("%d ", i); 
} 

int main() 
{ 
    Grid<unsigned> mygrid(.1,2,3); 
    unsigned ctr=0; 
    for(Grid<unsigned>::iterator it=mygrid.begin(); it!=mygrid.end(); ++it) 
    *it = ctr++; 

    ctr = 0; 
    printf("All elements: r, c, x, y, value\n"); 
    for(Grid<unsigned>::const_iterator it=mygrid.begin(); it!=mygrid.end(); ++it) { 
    assert(*it == ctr++); 
    printf("%d %d %f %f %d\n", it.r(), it.c(), it.x(), it.y(), *it); 
    } 

    printf("All elements values: "); 
    std::for_each(mygrid.begin(), mygrid.end(), print); 
    printf("\n"); 

    return 0; 
}