基本思想如下所示:
#include <vector>
#include <iostream>
template <class T>
class matrix {
size_t cols;
size_t rows;
std::vector<T> data;
class row_proxy { // This class is the part the question really asked about
size_t row;
matrix &m;
public:
row_proxy(matrix &m, size_t row) : row(row), m(m) {}
T &operator[](size_t col) {
if (row >= m.rows || col >= m.cols) // Note: row & col are indices not array count
throw std::logic_error("Bad index");
return m.data[row * m.cols + col];
}
};
public:
matrix(size_t cols, size_t rows) : rows(rows), cols(cols), data(rows*cols) {}
row_proxy operator[](size_t row) {
return row_proxy(*this, row);
}
};
int main() {
matrix<int> m(3, 3);
for (int i=0; i<3; i++) // fill the matrix with identifiable numbers
for (int j=0; j<3; j++)
m[i][j] = i * 100 + j;
for (int i=0; i<3; i++) { // show the content
for (int j=0; j<3; j++)
std::cout << m[i][j] << "\t";
std::cout << "\n";
}
try { // test the bounds checking.
m[4][1] = 21;
}
catch(std::logic_error &e) {
std::cerr << e.what();
}
return 0;
}
因此,当我们创建一个矩阵时,我们将其大小保存在rows
和中cols
。当我们operator[]
在矩阵上使用时,它不会尝试直接返回对矩阵中项目的引用——而是返回一个代理类的实例,该类跟踪行和矩阵,并提供operator[]
其自己的。
因此,当您使用 时matrix[a][b]
,第一个只是保存a
并matrix
进入代理对象。然后[b]
在该代理对象上调用该部分。这会检查a
和b
都在我们为矩阵保存的范围内,如果是,则返回对向量中正确对象的引用。否则,它会抛出一个std::Logic_error
(可能不是最好的选择——只是我想到的第一个)。
我应该补充一点,这个一般想法有很多变化。仅举一个例子,您可以在编译时指定数组的大小,但将大小作为模板参数传递。这可能有一些优点——例如,matrix<int, 2, 3>
它们matrix<int, 3, 2>
是完全不同的类型,所以你不能不小心将一个分配给另一个。它也可能有一些缺点(最明显的是,您需要在编译时知道大小,否则它根本不起作用)。