首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

C++ 类中的二维数组动态分配有关问题

2013-06-26 
C++ 类中的二维数组动态分配问题本帖最后由 yy198698 于 2013-06-20 11:25:35 编辑这是目前学习测试的一段

C++ 类中的二维数组动态分配问题
本帖最后由 yy198698 于 2013-06-20 11:25:35 编辑 这是目前学习测试的一段小代码,麻烦高手看看问题:
class Neuralnet 
{
private:

//单元网络的基本数目
int m_nInput;    //输入层单元数目
int m_nHidden;   //隐藏层单元数目
int m_nCase;     //打包的数据个数  

double **Input;   //输入层参数
double *yy;
public:
Neuralnet(int nInput, int nHidden, int nCase);
~Neuralnet();
void CreateNetwork(); //建立网络,为各层单元分配空间
void InitializeNetwork();
void shanchu();
};

主要是两个函数的问题:

void Neuralnet::CreateNetwork()
{
int i;
//初始化神经网络基本参数
Input = new double *[m_nCase];
yy = new double [m_nCase];

for (i=0;i<m_nCase;i++)
{
Input[i] = new double [m_nInput];
}
}

void Neuralnet::shanchu()
{
int i;
//删除参数空间
if(Input != NULL)
{
for (i=0; i<m_nCase;i++)
{
cout<<endl;
cout<<i;
delete[] Input[i];
Input[i]=NULL;
}

delete[] Input;
Input=NULL;
}
delete []yy;
}

下面是主函数
#include "test.h"

using namespace std;

int main()
{
int i,j;
Neuralnet test(4,5,3);
int row = 3;  
int col = 4;  
double ** array2D = new double*[row];  
for(i = 0; i < row; i++)  
array2D[i] = new double[col];  
for(i = 0; i < row; i++)   
for(j = 0; j < col; j++)  
array2D[i][j] = i+j; 
test.Calculate(array2D);
test.display();
test.shanchu();

for(int i = 0; i < row; i++){  
delete[] array2D[i];  
}  
delete []array2D;
return 0;
}


我按照网上给定的方法进行二维数组的动态分配和删除

动态分配的时候没有问题,但是在删除的时候,二维数组的里层 delete[] Input[i];没问题,执行到delete[] Input;的时候运行的时候报错,无法执行~~~,同样的代码在主函数中运行就没有这样的问题!
为了解决问题,我拿一维数组测试,就不存在类似的问题。。。。无解啊
求教大神们,在编写的时候是不是有些问题。
二维数组 C++ 类
[解决办法]
崩溃的时候在弹出的对话框按相应按钮进入调试,按Alt+7键查看Call Stack里面从上到下列出的对应从里层到外层的函数调用历史。双击某一行可将光标定位到此次调用的源代码或汇编指令处。

判断是否越界访问,可以在数组的最后一个元素之后对应的地址处设置数据读写断点。如果该地址对应其它变量干扰判断,可将数组多声明一个元素,并设置数据读写断点在该多出元素对应的地址上。

[解决办法]

单看这两段代码没有什么问题,应该是调用chanchu之前有过越界写访问,或者n_Case被改变了(变大),从而导致前面的释放代码越界,所以才导致delete失败。也有可能是某段代码改变了Input指针的值。
delete失败常见原因有:
1:被释放的内存块发生过越界写访问,破坏了内存块信息。
2:其它操作破坏了堆信息
3:内存块地址被改变了。
4:被释放的对象的析构函数中发生上述问题。本例中对象为double,不存在这种问题。

此外

delete[]yy;

这一句既然不在析构函数中,那么删除之后应该置为NULL,删除之前应判断。

实际上你的实现更象“数组的数组”,相当于Pascal中的array of array of double,而不是array [0..m_nCase-1, 0..m_nInput] of double这种标准的二维数组。


实现“二维数组”的更好的方法是使用线性的存储空间:

double * Input;
new double[m_nInput*m_nCase];
访问时:
Input[ row * m_nInput + col ];
释放时:
delete []Input;

当然如果你就是这种需要每行长度都可能不同的“数组的数组”那就只能循环为每行分配内存了。

建议把二维数组用一个类来实现,这样有几个好处:
1:可以实现“资源分配即初始化”,简化内存管理。
2:数组的数据指针和长度信息封装在数组对象中,不受其它代码影响,减少bug。
3:可以定义成员函数来访问数据,简化访问代码。比如用线性空间实现的版本,就可以把计算索引的代码放在成员函数或操作符中。
4:可以在访问代码中加入越界查检,便于查错。
5:便于重定义或禁止拷贝操作,以避免重复释放

我这里有一个线性空间的二维数组,我改了改,去掉了std::vector,改成了自己分配释放内存,你可以参考一下。



class double_array_2d
{
public:
  //缺省构造函数,空数组
  double_array_2d()
    : data(0)
, row_count(0)
, col_count(0)
  {
  }
  //构造函数,创建一个crow行ccol列的二维数组
  double_array_2d( int crow, int ccol )
    : data( new double[ crow * ccol ] )
, row_count( crow )
, col_count( ccol )
  {
  }
  //拷贝构造函数,分配空间并复制数据
  double_array_2d ( double_array_2d const & src )
    : data( new src.row_count * src.col_count )
, row_count( src.row_count )
, col_count( src.col_count )
  {
    for( int i = 0; i < row_count * col_count; ++ i )
{
   data[i] = src.data[i];
}
  }
  //析构时释放空间。
  ~double_array_2d()
  {
    if( data )
{
  delete []data;
}
  }
  //普通的访问方法,调用者需要保证不越界,调试版本用断言检查
  double & at( unsigned int row, unsigned int col )
  {
     assert( row < row_count && col < col_count );
 return data[ row * col_count + col ];
  }
  //普通的访问方法,常量版本,调用者需要保证不越界,调试版本用断言检查
  double at( unsigned int row, unsigned int col )
  {
     assert( row < row_count && col < col_count );
 return data[ row * col_count + col ];
  }
  
  //下面实现operator[ ],也可以不实现,只用at进行访问。
  //这个类是辅助operator[ ]用的,因为operator[ ]只接收一个参数。
  class row_accessor
  {
  public:
     row_accessor( double * data, unsigned int length )
       : my_data(data)
   , my_length(length)
   {
   }
 double & operator[] ( unsigned int col ) throw()
 {
    assert( col < my_length );
return data[ col ];
 }
 double operator[] ( unsigned int col ) const throw()
 {
    assert( col < my_length );
return data[ col ];
 }
  };
  
  row_accessor operator[]( int row ) throw()
  {


     return row_accessor( data[ row ] );
  }
  
  const row_accessor operator[]( int row ) const throw()
  {
     return row_accessor( row );
  }
  
  //交换两个数组的数据
  //拷贝构造函数和redim通过调用这个方法实现。
  //这是除了构造与析构以外,唯一直接改变data、row_count与col_count的成员函数
  //其改变也是在两个构造完成的array之间进行交换,并且是长度与指针信息一同交换,
  //从而保证内存分配与释放的安全性
  void swap( double_array_2d & other ) throw()
  {
     unsigned int crow = row_count;
 unsigned int ccol = col_count;
 double * my_data = data;
 data = other.data;
 row_count = other.row_count;
 col_count = other.col_count;
 other.data = my_data;
 other.row_count = crow;
 other.col_count = ccol;
  }
  //重新分配,可以增加代码以保持数组中的数据,用构造函数+swap实现
  void redim( unsigned int crow, unsigned ccol )
  {
    double_array_2d temp( crow, ccol );
//可以添加代码把data中的数据或一部分数据拷贝到temp中。
//拷贝时要用二重循环,用at或operator[]访问数据
temp.swap( * this );
  }
  //拷贝赋值操作符,用拷贝构造函数和swap实现
  double_array_2d & operator = ( double_array_2d const & other )
  {
    double_array_2d temp( other );
temp.swap( * this );
  }
  
private:
  unsigned int row_count;
  unsigned int col_count;
  double * data;
private:
  //如果不想实现拷贝构造和拷贝赋值,可以用下面的代码禁止拷贝操作
  //这时前面的拷贝构造函数和拷贝赋值操作符的定义就不需要了
  //double_array_2d ( double_array_2d const & src );
  //double_array_2d & operator = ( double_array_2d const & other );
};



[解决办法]
18 - 29行:

  //拷贝构造函数,分配空间并复制数据
  double_array_2d ( double_array_2d const & src )
    : data( new double[ src.row_count * src.col_count ] )
    , row_count( src.row_count )
    , col_count( src.col_count )
  {
    for( int i = 0; i < row_count * col_count; ++ i )
    {
       data[i] = src.data[i];
    }
  }


73 - 82行:

  row_accessor operator[]( int row ) throw()
  {
     return row_accessor( data[ row ] );
  }
   
  const row_accessor operator[]( int row ) const throw()
  {
     return row_accessor( row );
  }

[解决办法]


73 - 82行:


  row_accessor operator[]( int row ) throw()
  {
     return row_accessor( data[ row ], col_count );
  }
   
  const row_accessor operator[]( int row ) const throw()
  {
     return row_accessor( data[ row ], col_count );
  }

热点排行