STL:连续空间的二维数组实现
Posted 小键233
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STL:连续空间的二维数组实现相关的知识,希望对你有一定的参考价值。
这个不属于STL 中的内容,仅是个人补充使用
2019-07-02 修改:
添加了相关文档的链接:https://blog.csdn.net/u014613043/article/details/94492607
嘛~一开始是这样的。
假如我们需要新建一个二维数组,一般像下面这样用:
int row = 2, col = 3;
int array[2][3]; //1
//or
vector< vector<int> > v(row, vector<int>(col) );//2
//or
int* array = new int[row*col];//3
//or
int** array = new int*[row]; //4
//循环new
但是,如果在java 里面,一句搞定:
int[][] a = new int[row][col];
嗯,不得不说,实在是方便。事后也不需要delete 。
but,java 能够这么傲娇的使用,也是基于引用计数的。这有成本。
那讨论一下C++ 几种方式。
就1而言,只能是静态指定,不能在程序中动态分配,差评。
2 算是比较好的选择,但是,她会浪费掉空间。首先是初始化的时候,就会产生一个 vector<int>(3)
的副本。其次,每个维度的vector 都会有额外的三个迭代器空间浪费掉。空间的利用率不高。
3是用一维的数组进行动态分配,然后用指针的偏移量来访问。它的优点很多,空间连续,没有空间浪费,适用于任何的高维数组。但是缺点也很明显,需要动态回收,需要手动计算偏移量,不够直观。
4和3基本是一样的,但是要做的工作更多,循环new 和循环delete 必不可少,在我看来实在是糟糕。
我一般避免在程序的逻辑中直接new 和delete。暴露过多的new 和delete 实在不是一个好的习惯,一不小心就会忘记回收内存了。而且,万一在delete 语句之前就return 的话,内存就泄露了。
我看到java 的使用方式简洁到让我嫉妒,于是就想着自己写个二维的数组吧,基于连续的空间分配。
为什么我一直强调连续的空间呢?嗯~大概是个人喜好吧。
首先空间连续,就没有空间浪费。
其次,分配和回收内存的动作一步到位,不懂拆分成多步。
最后,不管任何情况下,都能拿出原生指针,自己做最基本的C++ 操作。
扯那么多,下面来看看怎么实现。
##array2
我将类命名为array2。
在开始写之前,要明确一些需求:
- 我希望能够在程序中动态生成二维数组
- 希望分配的空间内存连续
- 希望能够自动申请和回收内存
- 提供方便的下标访问
更高级一点的:
- 能够指定迭代器,会数组赋值
- 表现得越像原生指针越好
明确需求之后,就可以一步一步地编码了。
###如何访问元素
这个问题我想了好久,最后敲定了现在的解决方案。
最理想的情况是,能够使用[][]
进行访问。
但是,[] 只能接受一个参数,如果这么用的话,必须要加工。
一个方法是,让array2 operator [] return 一个结构的,那个结构再重载operator[] 从而实现元素的访问。
大概类似于这样
template<typename T>
struct _array1
T* data;
array1(T* _data):data(_data)
T& operator [] (int i) return data[i];
;
//array2
template<typename T>
class array2
_array1 operator [] (int i)
//...
return _array1(data); //把计算的偏移量放进来
;
这样子的好处是显而易见的。能够像原生指针一样访问元素:
array2<int> x(3,4); //3 行4列
x[1][2];
但是也有明显不好的地方,就是每次的下标访问,都会有_array1 的构造和析构。在循环中,感觉不妙。
而且,当实现三维的时候怎么办,需要两次的类过渡。
其实这不失为一个好的方法,但是为了一致性,我们只能寻找另外一种方法了
**operator () **
重载括号,能够接受自己设定的参数数量。nice 就它了。
###构造函数
受vector 的影响,array2 中也用了各种typedef ,构造函数的形式和很像:
/** 二维数组
*/
template<typename T, typename Alloc=alloc>
class array2
public:
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef T* iterator;
typedef const T* const_iterator;
private:
typedef simple_alloc<T,Alloc> data_alloc;
enum dimension_size = 2 ;
pointer _data;
size_type _dimension[dimension_size]; //记录维数信息
//....
public:
array2() : _data(0)
array2(size_type row, size_type col):_data(0)
init(row, col, value_type() );
array2(size_type row, size_type col, const value_type& val):_data(0)
init(row, col, val);
template<typename Input_iter>
array2(size_type row, size_type col, Input_iter first):_data(0)
init(row, col, first);
array2(const array2& x)
init(x.rows(), x.cols(), x.data() );
//....
其中init 如下:
/** init
*/
void init(size_type row, size_type col)
init(row, col,value_type() );
void init(size_type row, size_type col, const value_type& val)
_clear();
set_dimension(row, col);
allocate_and_fill(_dimension[0], val);
template<typename Input_iter>
void init(size_type row, size_type col, Input_iter first)
_clear();
set_dimension(row, col);
allocate_and_copy(_dimension[0], first);
用到的几个辅助函数如下:
//--------------------function------------
void allocate_and_fill(size_type _s, const value_type& val)
_data = data_alloc::allocate(_s);
uninitialized_fill_n(_data, _s, val);
template<typename Input_iter>
void allocate_and_copy(size_type _s, Input_iter first)
_data = data_alloc::allocate(_s);
uninitialized_copy_n(first, _s, _data);
void _clear()
if(_data!=0)
destroy(_data, _data + _dimension[0]);
data_alloc::deallocate(_data, _dimension[0]);
_data = 0;
void set_dimension(size_type row, size_type col)
_dimension[1] = col;
_dimension[0] = row*col;
init 的可以重新设定数组的维度和内容,就相当于重来一遍。
dimension 中存储维度信息,大小为2:
row*col , col
为什么这么设计呢?
首先,size 的信息直接可以拿到(row*col), 然后另一个考虑是为了一致性。
考虑正在设计三维的数组:
dimension[3];
//x,y,z 表示维度, int[x][y][z]
dimension[2] = z;
dimension[1] = y*dimension[2];
dimension[0] = x*dimension[1];
这样子有什么好处呢?考虑到这时候我要访问[i][j][k]
的元素:
return data[i*dimension[1] + j*dimension[2] +k ];
//原来的如下:
return data[i*y*z + j*z + k];
明白没有?这样子可以省去(y*z) 的计算。
当维度越來越高时,能够省去的计算也就越多。
###访问元素
访问元素就用operator () 进行:
/** operator ()
*/
reference operator () (size_type x, size_type y)
return _data[x * _dimension[1] + y];
const_reference operator () (size_type x, size_type y) const
return _data[x* _dimension[1] + y];
其他的设计倒是稀松平常,源代码在文末贴上。
##如何表现得像一个原生指针
我们希望,下面的代码能够被支持:
array2<int> x(3,4);//尽量让x 相当于一个int*
int i = *(x+2);
int e = x[23];
在使用的时候,要知道,其实这是一维的数组,抽象上的哦二维数组。所以x 的表现就像一个int*
那么实现和类型转换函数就可以了:
/** operator pointer
*/
operator pointer ()
data();
operator const pointer () const
data();
##源代码
/** 二维数组
*/
template<typename T, typename Alloc=alloc>
class array2
public:
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef const T& const_reference;
typedef size_t size_type;
typedef T* iterator;
typedef const T* const_iterator;
private:
typedef simple_alloc<T,Alloc> data_alloc;
enum dimension_size = 2 ;
pointer _data;
size_type _dimension[dimension_size]; //记录维数信息
//--------------------function------------
void allocate_and_fill(size_type _s, const value_type& val)
_data = data_alloc::allocate(_s);
uninitialized_fill_n(_data, _s, val);
template<typename Input_iter>
void allocate_and_copy(size_type _s, Input_iter first)
_data = data_alloc::allocate(_s);
uninitialized_copy_n(first, _s, _data);
void _clear()
if(_data!=0)
destroy(_data, _data + _dimension[0]);
data_alloc::deallocate(_data, _dimension[0]);
_data = 0;
void set_dimension(size_type row, size_type col)
_dimension[1] = col;
_dimension[0] = row*col;
void _check_range(size_type row, size_type col)
if(row * _dimension[1] + col >= _dimension[0] )
printf(" In array2, index out of range!\\n");
abort();
public:
array2() : _data(0)
array2(size_type row, size_type col):_data(0)
init(row, col, value_type() );
array2(size_type row, size_type col, const value_type& val):_data(0)
init(row, col, val);
template<typename Input_iter>
array2(size_type row, size_type col, Input_iter first):_data(0)
init(row, col, first);
array2(const array2& x)
init(x.rows(), x.cols(), x.data() );
~array2()
_clear();
/** init
*/
void init(size_type row, size_type col)
_clear();
init(row, col,value_type() );
void init(size_type row, size_type col, const value_type& val)
_clear();
set_dimension(row, col);
allocate_and_fill(_dimension[0], val);
template<typename Input_iter>
void init(size_type row, size_type col, Input_iter first)
_clear();
set_dimension(row, col);
allocate_and_copy(_dimension[0], first);
/** size
*/ size_type size() const return _dimension[0];
/** at
*/
reference at(size_type x, size_type y)
_check_range(x, y);
return operator () (x, y);
const_reference at(size_type x, size_type y) const
_check_range(x, y);
return operator () (x, y);
/** begin and end
*/
iterator begin() return _data;
const_iterator begin() const return _data;
iterator end() return _data+_dimension[0];
const_iterator end() const return _data+_dimension[0];
/** operator ()
*/
reference operator () (size_type x, size_type y)
return _data[x * _dimension[1] + y];
const_reference operator () (size_type x, size_type y) const
return _data[x* _dimension[1] + y];
/** data
*/
pointer data() return _data;
const pointer data() const return _data;
/** row and col dimension
*/
size_type rows() const return _dimension[0]/_dimension[1];
size_type cols() const return _dimension[1];
size_type dimension(int i) const
if(i==0) return rows();
else if(i==1) return cols();
else return 0;
/** assign_elem
*/
template<typename Input_iter>
void assign_elem(const_iterator pos, Input_iter first, Input_iter last)
copy(first, last, const_cast<iterator>(pos) );
/** operator ostream
*/
friend std::ostream& operator << (std::ostream& out, const array2& x)
out<<"[\\n";
int row = x._dimension[0] / x._dimension[1];
int index = 0;
for(int i=0; i<row; ++i)
for(int j=0; j<x._dimension[1]; ++j, ++index)
out<<x._data[index]<<" ";
out<<endl;
out<<"] "<<endl;
return out;
/** operator pointer
*/
operator pointer ()
data();
operator const pointer () const
data();
/** operator =
*/
array2& operator = (const array2& x)
if(&x != this)
if(size() == x.size() )
assign_elem(begin(), x.begin(), x.end() );//TO DO
else
init(x.rows(), x.cols(), x.data());
return *this;
;
欢迎拍砖
以上是关于STL:连续空间的二维数组实现的主要内容,如果未能解决你的问题,请参考以下文章