【问题标题】:Implementing custom iterator for a dynamic matrix class为动态矩阵类实现自定义迭代器
【发布时间】:2016-11-04 20:53:00
【问题描述】:

考虑以下类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;
};

下面定义了使用std::bidirectional_iterator_tag 的自定义迭代器dynamic_matrix_iterator

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;
};

这是一个测试用例,我使用基于范围的 for 循环来打印矩阵中的元素,并使用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);
}

现在,基于迭代器的范围 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 的指针访问“随机”时引起的矩阵的每个行向量之间的内存 - 在不同的机器上运行它可能会在这里产生不同的值,或者如果这是我怀疑的 未定义的行为,则会产生其他意想不到的东西。

问题

那么考虑到这种行为,有没有更优雅的方式来实现这个容器的迭代器?此外,鉴于 std::vector 使用连续存储,为什么上述实际发生 - 向量内部存储器的“垃圾”值是否用于允许向量扩展?

【问题讨论】:

  • 我确实考虑过将它发布到 codereview,但是在代码编译和运行时,输出对于我的具体问题是不正确的,所以我认为最好在这里发布。
  • 你说得对。
  • “鉴于 std::vector 使用连续存储,为什么上述实际发生” - 因为尽管向量存储在连续内存中,但这里的向量远不止一个。你有一个向量向量,主要向量的内存确实是连续的,存储行。任何给定的行向量确实是连续的,存储其元素。但是行的元素的集合并不连续彼此
  • @WhozCraig 我明白了,这是有道理的。
  • 如果您将结构从 vector&lt;vector&lt;T&gt;&gt; 修改为 vector&lt;T&gt; 并管理 rowcolumn 计数,您可以通过单个 vector 来解决问题,事情会更清楚.插入和删除不会那么直接,但因为您可以轻松地迭代单个向量,所以并不难。

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


【解决方案1】:

向量的向量不连续。所以你的方法被打破了。

要么使用平面向量并单独维护维度,要么为范围范围编写通用迭代器。

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

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

从前面收缩的地方。

取消引用是“从内部范围的前面获取”。

Here is an earlier post where I implemented this strategy.

【讨论】:

  • 为此干杯,我决定将我的结构更改为从其他反馈中以行主要格式使用单个 std::vector,但我相信如果我需要,您的回答会派上用场类似的数据结构。
猜你喜欢
  • 1970-01-01
  • 2019-12-26
  • 2018-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-22
  • 2018-09-06
  • 1970-01-01
相关资源
最近更新 更多