首页 > 代码库 > 多维数组的索引越界问题

多维数组的索引越界问题

1、我们大都知道可以使用vector或array模板作为线性数组的实现,那么对于需要二维矩阵、三维数组(或者N维数组)时应该怎么解决。

由于N维数组的基本情况中的所有问题都可以用一个二维矩阵举例说明,因此以下的讨论仅限于此,并简单的称为矩阵。

如果矩阵的大小在编译时是已知的,可以很方便的把它实现为数组的数组,这个很简单。这里,我们主要把注意力集中在当矩阵的大小是在运行时计算产生,对于这种复杂的情况,我们可以很方便的使用vector或的vector来实现。事实上,如果不同的行必须必须具有不同的长度,这也是唯一可行的方法。但是,在大多时候,所有的行应该具有相同的长度,此时使用vector的vector是一种效率不高的方法:它需要多次分配内存,这是相对较慢的操作。由于我们使用C++的一个重要原因是追求高效,因此我们尝试一种不同的方法,即只通过一次内存分配来创建一个长方形的矩阵。

示例代码:

//二维长方形矩阵
template <typename T>
class matrix
{
public:
	typedef unsigned size_type;

	matrix(size_type num_rows, size_type num_cols)
		:rows_(num_rows),cols_(num_cols),data_(num_rows*num_cols)
	{
		SCPP_TEST_ASSERT(num_rows > 0,
			"Number of rows in a matrix must be positive");
		SCPP_TEST_ASSERT(num_cols > 0,
			"Number of columns in a matrix must be positive");
	}

	matrix(size_type num_rows, size_type num_cols, const T& init_value)
		:rows_(num_rows),cols_(num_cols),data_(num_rows*num_cols,init_value)
	{
		SCPP_TEST_ASSERT(num_rows > 0,
			"Number of rows in a matrix must be positive");
		SCPP_TEST_ASSERT(num_cols > 0,
			"Number of columns in a matrix must be positive");
	}

	size_type num_rows() const { return rows_; }
	size_type num_cols() const { return cols_; }

	//访问方法:返回由行和列所指定的元素
	T& operator() (size_type row, size_type col)
	{
		return data_[index(row,col)];
	}

	const T& operator() (size_type row, size_type col) const
	{
		return data_[index(row,col)];
	}

private:
	size_type rows, cols;
	std::vector<T> data_;

	size_type insex(size_type row, size_type col) const
	{
		SCPP_TEST_ASSERT(row < rows_, "Row " <<row<<" must be less than "<<rows_);
		SCPP_TEST_ASSERT(col < cols_, "Column  " <<col<<" must be less than "<<cols_);
		return cols_* row + col;
	}
};

首先,这个类中有2个构造函数。第一个构造函数允许我们用指定的行数和列数创建一个矩阵。第二个构造函数具有一个额外的init_value参数,允许把每个元素初始化为一个指定的值(例如,把一个matrix<double>的每个元素设置为0.0)。注意,对元素的访问时通过()操作符进行的。这是因为C++的[]操作符只接受1个参数,不能接受2个参数或更多的参数。因此,为了访问多维数组,我们或者使用多个[](例如my_matrix[i][j]),或者使用1个()操作符(例如my_matrix(i,j))。

如果我们让[]操作符返回指向第i行第0个元素的T*类型的指针,就可以实现第一种方法。但是,这样就无法诊断列索引越界的问题,违背了在运行时捕捉缺陷的初衷。当然,我们可以创建一些模板类,包括一个指向一个列的智能引用,并使用第一个操作符([i])返回它的一个实例,并在第二个操作符([j])中使用边界检查。在某种程度上,这是因人的习惯。我们看不到仅仅为了保留my_matrix[i][j]的语法而采用这种复杂设计的价值,显然具有多个参数的()操作符看上去更为直观。

对索引越界的检查是在index(row,col)函数中执行的,这两个参数分别表示行数和列数。如果出现了运行时错误,就会导致一个错误处理函数被调用。

最后,在示例代码最后,提供一个matrix<T>模板的<<操作符,是为了输出矩阵:

cout<<"my matrix = \n"<<my_matrix<<endl;

但是,前提是这个矩阵不能太大,并且类型T定义了<<操作符。


总结:避免“索引越界”的规则:

  • 不要使用静态或动态分配的数组,可以改用array或者vector模板
  • 不要使用带方括号的new和delete操作符,让vector模板为多个元素分配内存
  • 使用scpp::vector代替std::vector,使用scpp::array代替静态数组,并打开安全检查
  • 对于多维数组,使用scpp::matrix,并通过()操作符访问元素,已提供索引越界检查。