为动态矩阵类实现自定义迭代器

时间:2016-07-02 16:51:44

标签: c++ vector stl iterator c++14

考虑以下类dynamic_matrix的一部分,它是一个封装std::vector<std::vector<T>>的容器,其中包含一个不变量,表明每一行应具有相同数量的元素,并且每列应具有相等数量的元素。该课程的大部分内容已被删除,因为大多数部分与此问题无关。

dynamic_matrix

template<typename _Ty>
class dynamic_matrix {
public:
    // public type defns
    typedef _Ty value_type;
    typedef _Ty& reference;
    typedef const _Ty& const_reference;
    typedef _Ty* pointer;
    typedef const _Ty* const_pointer;
    typedef std::size_t size_type;
    typedef std::ptrdiff_t difference_type;
    typedef dynamic_matrix_iterator<value_type> iterator; // defined below
private:
    typdef std::vector<std::vector<value_type>> vector_2d;
    // enables use of operator[][] on dynamic_matrix
    class proxy_row_vector {
    public:
        proxy_row_vector(const std::vector<value_type>& _vec) : vec(_vec) {}
        const_reference operator[](size_type _index) const {
            return vec[_index];
        }
        reference operator[](size_type _index) {
            return vec[_index];
        }
    private:
        std::vector<value_type>& vec;
    };
public:
    explicit dynamic_matrix() : mtx() {}
    template<class _Uty = _Ty,
        class = std::enable_if_t<std::is_default_constructible<_Uty>::value>
    > explicit dynamic_matrix(size_type _rows, size_type _cols) 
       : mtx(_row, std::vector<value_type>(_cols)) {}
   // ... a few other constructors, not important here...

   // Capacity

   bool empty() const noexcept {
       return mtx.empty();
   }
   size_type rows() const noexcept {
       return mtx.size();
   }
   size_type columns() const noexcept {
       if(empty()) return static_cast<size_type>(0);
       return mtx[0].size();
   }

   // Element access       

   proxy_row_vector operator[](size_type _row_index) const {
       return proxy_row_vector(mtx[_row_index]);
   }
   proxy_row_vector operator[](size_type _row_index) {
       return proxy_row_vector(mtx[_row_index]);
   }
   const value_type* inner_data(size_type _row_index) const noexcept {
       return mtx[_row_index).data();
   }
   value_type* inner_data(size_type _row_index) noexcept {
       return mtx[_row_index].data();
   }
   std::ostream& write(std::ostream& _os, char _delim = ' ') const noexcept {
       for (const auto& outer : mtx) {
           for (const auto& inner : outer) 
               _os << inner << _delim;
           _os << '\n';
       }
       return _os;
   }

   // Iterators

   iterator begin() {
       return iterator(inner_data(0)); // points to first element of matrix
   }
   iterator end() {
       // points to element past end of matrix
       return iterator(inner_data(rows()-1) + columns());
   }
private:
    vector_2d mtx;
};

使用dynamic_matrix_iterator的自定义迭代器std::bidirectional_iterator_tag定义如下。

dynamic_matrix_iterator

template<typename _Ty>
class dynamic_matrix_iterator : public std::iterator<std::bidirectional_iterator_tag,
    _Ty, std::ptrdiff_t, _Ty*, _Ty&> {
public:
    dynamic_matrix_iterator(_Ty* _ptr) : ptr(_ptr) {}
    dynamic_matrix_iterator(const dynamic_matrix_iterator& _other) = default;
    dynamic_matrix_iterator& operator++() {
        ptr++;
        return *this;
    }
    dynamic_matrix_iterator operator++(int) {
        dynamic_matrix_iterator<_Ty> tmp(*this);
        operator++();
        return tmp;
    }
    dynamic_matrix_iterator& operator--() {
        ptr--;
        return *this;
    }
    dynamic_matrix_iterator operator--(int) {
        dynamic_matrix_iterator<_Ty> tmp(*this);
        operator--();
        return tmp;
    }
    _Ty& operator*() {
        return *ptr;
    }
    _Ty* operator->() {
        return ptr;
    }
    bool operator==(const dynamic_matrix_iterator& _other) {
        return ptr == _other.ptr;
    }
    bool operator!=(const dynamic_matrix_iterator& _other) {
        return ptr != _other.ptr;
    }
private:
    _Ty* ptr;
};

这是一个测试用例,其中我使用基于范围的循环来打印矩阵中的元素,以及使用dynamic_matrix::write()方法进行比较:

int main(void) {
    std::size_t rows = 3;
    std::size_t cols = 3;
    dynamic_matrix<int> dm(rows,cols);
    int count = 0;
    // assign increasing natural numbers to each element
    for (std::size_t i = 0; i < rows; ++i) {
        for (std::size_t j = 0; j < cols; ++j) 
            dm[i][j] = ++count;
    }
    int range_count = 0;
    // print using iterators
    for (auto x : dm) {
        std::cout << x << ' ';
        ++range_count;
        if (!(range_count % cols))
            std::cout << std::endl;
    }
    std::cout << std::endl;
    // print using inbuilt method
    dm.write(std::cout);
}

现在,基于迭代器的ranged for循环打印出以下内容:

1 2 3
0 0 0
35 0 4
5 6 0
0 0 35
0 7 8 
9

dynamic_matrix::write给出的正确输出当然是

1 2 3
4 5 6
7 8 9

在使用迭代器的错误输出中,我们看到实际矩阵元素之间存在一些垃圾元素,当dynamic_matrix_iterator的指针访问时,我只能假设它是由未定义的行为引起的。 #34;随机&#34;矩阵的每个行向量之间的内存 - 在不同的机器上运行它可能会产生不同的值或者其他意外的东西,如果这是未定义的行为,我怀疑它是。

问题

因此,鉴于此行为,是否有更优雅的方法来实现此容器的迭代器?另外,假设std::vector使用连续存储,为什么上面实际发生了 - &#34;垃圾&#34;重视部分向量&#39;用于允许向量扩展的内部存储器?

1 个答案:

答案 0 :(得分:2)

载体的载体不是连续的。所以你的方法就被打破了。

使用平面向量并单独维护尺寸,或者为范围范围编写通用迭代器。

第二个最好用范围抽象来完成。存储两个范围(它们是迭代器对)。 ==比较外部范围开始和内部范围(所有空范围比较相等)。进步是:

shrink inner range
while inner range is empty
  shrink outer range, get new inner range from front of outer
repeat

初始化与外部范围:

Init outer range
While outer is non-empty And (inner range=front of outer) is empty 
  shrink outer range

从前面萎缩的地方。

取消引用是从内部范围前面来的#34;。

Here is an earlier post where I implemented this strategy