【问题标题】:out of bound check for multidimensional arrays多维数组的越界检查
【发布时间】:2013-03-14 05:29:28
【问题描述】:

我正在阅读一本关于 Safe C++ 的书。在这里,作者提到了如何避免超出范围的数组读取。在这里,提到了我们如何避免多维数组的越界数组读取。这里作者使用operator()函数如下链接所示代替operator[]并给出以下解释。

https://github.com/vladimir-kushnir/SafeCPlusPlus/blob/master/scpp_matrix.hpp

这里请注意,要访问多维数组,我们要么需要使用多个[] 运算符,例如matrix[i][j],要么需要使用单个() 运算符,例如matrix(i,j)。

如果我们让[] 运算符返回指向第 i 行第零个元素的T* 指针,则可以实现第一种方法。但是,这否定了我们对列索引越界的诊断,这违背了在运行时捕获错误的目的。当然,我们可以创建一些包含对行的智能引用的模板类,返回使用第一个运算符([i])的实例,然后在第二个运算符([j])中使用边界检查。

我的问题是作者所说的“创建一些包含对行的智能引用的模板类,返回使用第一个运算符([i])的实例,然后在第二个操作员 ([j])。" ?要求提供示例代码我们如何在 C++ 中实现上述逻辑?

感谢您的时间和帮助。

【问题讨论】:

  • 您能否用您目前在这方面的努力启发我们
  • 当问题是“这是什么意思”时,到目前为止如何努力?
  • 作者的意思是您返回一个对象,该对象包含对索引行开头的嵌入式引用,并且在该对象内,大小限制因此它(对象)被覆盖 operator [] 可以边界检查索引该维度类似于第一个维度的索引方式。
  • 就用第二种方法。 [][] 被高估了。

标签: c++


【解决方案1】:

基本的想法是这样的:

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

因此,当我们创建矩阵时,我们将其大小保存在 rowscols 中。当我们在矩阵上使用operator[] 时,它不会尝试直接返回对矩阵中项目的引用——而是返回一个代理类的实例,该实例跟踪行和矩阵,并且提供了自己的operator[]

所以,当您使用matrix[a][b] 时,第一个只是将amatrix 保存到代理对象中。然后在该代理对象上调用 [b] 部分。这将检查ab 是否都在我们为矩阵保存的范围内,如果是,则返回对向量中正确对象的引用。否则,它会抛出一个std::Logic_error(可能不是最好的选择——只是我想到的第一个)。

我应该补充一点,这个总体思路有很多变体。仅举一个例子,您可以在编译时指定数组的大小,但将大小作为模板参数传递。这可能有一些优势——例如,matrix&lt;int, 2, 3&gt;matrix&lt;int, 3, 2&gt; 是完全不同的类型,因此您不会意外地将一个分配给另一个。它也可能有一些缺点(最明显的是,您需要在编译时知道大小,否则它根本不起作用)。

【讨论】:

  • 感谢您的详细解释。现在我明白作者的意思了。
猜你喜欢
  • 1970-01-01
  • 2018-06-21
  • 2018-04-03
  • 2021-12-30
  • 2014-03-04
  • 2013-12-09
  • 2012-03-31
  • 1970-01-01
  • 2019-11-02
相关资源
最近更新 更多