【发布时间】: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<vector<T>>修改为vector<T>并管理row和column计数,您可以通过单个vector来解决问题,事情会更清楚.插入和删除不会那么直接,但因为您可以轻松地迭代单个向量,所以并不难。
标签: c++ vector stl iterator c++14