2013-03-02 110 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; 
}