【问题标题】:Deallocating Dynamic 2D Array with Template使用模板释放动态二维数组
【发布时间】:2011-07-08 02:27:07
【问题描述】:

我的 C++ 应用程序出现错误,我无法调试。我在网上查看过,我似乎正在以正确的方式进行所有分配/解除分配。这是我的代码:

template <typename T>
class Matrix
{
private:
   int _rows;
   int _cols;
   T** _matrix;
public:
   Matrix(int r, int c);
   ~Matrix();
   T GetValue(int r, int c);
};

template <typename T>
Matrix<T>::Matrix(int r, int c)
{
   _rows = r;
   _cols = c;

   _matrix = new T*[_rows];
   for(int i = 0; i < _rows; i++)
      _matrix[i] = new T[_cols];

   for(int i = 0; i < _rows; i++)
      for(int j = 0; j < _cols; j++)
         _matrix[i][j] = NULL;
}

template <typename T>
Matrix<T>::~Matrix()
{
   for(int i = 0; i < _rows; i++)
      delete [] _matrix[i];
   delete [] _matrix;
}

template <typename T>
T Matrix<T>::GetValue(int r, int c)
{
   if(r < 0 || r >= _rows || c < 0 || c > _cols)
   {
      throw -1;
      return NULL;
   }

   return _matrix[r][c];
}

还有我的客户端代码...

int main()
{
   Matrix<int> myMatrix(3, 3);
   myMatrix.GetValue(1, 1);
   // myMatrix.~Matrix();  // Don't do this anymore
}

一旦变量“myMatrix”超出范围,我就会收到以下错误:

0x103159da (msvcr1000d.dll) 处的未处理异常...访问冲突读取位置 0xfeeefee2。
我被带到文件“dbgdel.cpp” _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));

请帮忙!


编辑:

好的,我忽略了提供一些信息。请看下面:

我还有一个名为“T Dot(Matrix)”的附加方法我还有两个名为“Columns()”和“Rows()”的方法,它们只是 _cols 和 _rows 的 getter。还有一个名为“SetValue(int r, int c, T value)”的方法,它设置_matrix[r][c] = value。我不认为展示这些实现是必要的。

template <typename T>
T Matrix<T>::Dot(Matrix<T> m)
{
   if(_cols > 1 || m.Columns() > 1 || _rows != m.Rows())
   {
      throw -1;
      return NULL;
   }

   T value = 0;
   for(int i = 0; i < _rows; i++)
   {
      value += _matrix[i][0] * m.GetValue(i, 0);
   }
   return value; // Whoops, this was here, just forgot to type it
}

还有客户……

int main()
{
   Matrix<int> intM1(3, 1);
   Matrix<int> intM2(3, 1);

   intM1.SetValue(0, 0, 1);
   intM1.SetValue(1, 0, 1);
   intM1.SetValue(2, 0, 1);
   intM2.SetValue(0, 0, 1);
   intM2.SetValue(1, 0, 1);
   intM2.SetValue(2, 0, 1);

   std::cout << intM1.Dot(intM2) << endl;
}

这会产生与上述相同的错误,但仅在调用“Dot()”函数时。

【问题讨论】:

  • 你试过从你的主函数中删除 myMatrix.~Matrix() 吗?一旦对象超出范围,就会自动调用析构函数,您不应该显式调用它。
  • @Connman。谢谢,我知道析构函数是自动调用的,但我把它放在那里作为测试。我可以发誓即使我没有错误,我也会收到错误,但是现在我问了这个问题,它们当然已经消失了。我将尝试重现几个小时前的内容。
  • @Eric,如果您不使用它,请不要在您的问题中添加此类误导性代码。请参阅编辑后的答案。

标签: c++ arrays memory-management dynamic


【解决方案1】:

不要显式调用析构函数,您已经在堆栈上分配了myMatrix,因此当变量超出范围时,即main() 返回时,将自动调用dtor

【讨论】:

  • @Templatetypedef: 是的,我能想到的唯一的地方你会明确调用dtor是清理使用放置new创建的对象...
  • 谢谢大家的好心cmets。如果可能,请查看我的编辑并提供额外的 cmets。
【解决方案2】:

通过您的编辑,这里的问题仍然是缺少复制构造函数和赋值运算符(请参阅我对您最后一个问题的回答)。问题是,当您将Matrix 按值传递给函数时,会调用复制构造函数来进行复制,但由于您尚未定义一个 C++ 将使用默认的复制构造函数,这只会进行浅拷贝。因此,您最终会得到一个新的Matrix,它与旧的Matrix 共享一个指向相同元素的指针。当这个新的Matrix 超出范围时,它的析构函数将触发,清理另一个Matrix 使用的数组。当原来的Matrix然后超出范围并被清理时,它会尝试删除一个已经删除的数组,从而导致崩溃。

要解决此问题,您需要实现正确复制资源的复制构造函数和赋值运算符。有一个经验法则叫做三法则,它说如果你有一个析构函数,你还需要一个复制构造函数和赋值运算符来防止这些错误。尝试实现这些缺失的功能,看看问题是否解决。

【讨论】:

  • 感谢您为解决此问题提供的出色解释和指导。很抱歉之前的混乱。
  • 虽然这完全正确,但您可能需要考虑是否真的要将矩阵的副本作为参数传递给Dot。我希望这是一个const 参考:T Matrix&lt;T&gt;::Dot(Matrix&lt;T&gt; const&amp; m) // etc。这样您就不会在每次调用 Dot 时复制整个矩阵。
  • @Darren Engwirda- 绝对。尽管如此,仍应尽快处理丢失的复制功能,以防止此类错误出现在看似有效的代码中。
  • @Darren Engwirda:谢谢,几分钟前我真的在考虑这个问题。我很可能会将其设为常量。但是,我不知道“三法则”,所以我肯定会阅读有关它的内容。如果有人碰巧在读这篇文章,这里是维基百科页面:en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)
【解决方案3】:

编辑

这是错误:

T Matrix<T>::Dot(Matrix<T> m);

这个函数应该返回T,但它最终没有返回!!放,

return value;

而且它应该得到解决。还有一些建议:您通过值传递Matrix&lt;T&gt; m;,这很容易出错。因为复制的m 和原始的intM2 都指向相同的_matrix。因此,当m 超出范围时,它将delete[] 一切。当intM2 超出范围时,它将再次删除相同的内存。这又是一次崩溃。

因此,您应该始终指定复制构造函数(private 或使用正确的复制代码)。截至目前,将定义更改为,

T Matrix<T>::Dot(Matrix<T> &m); // pass `m` by reference

这将解决您的所有错误。 (您也可以选择通过const Matrix&lt;T&gt; &amp;m,因为您可能需要更改GetValue () const。)

【讨论】:

  • 谢谢。我感觉很糟糕,因为我不小心问错了问题。您能否阅读我的编辑并提供您的专业知识?
猜你喜欢
  • 2022-01-15
  • 1970-01-01
  • 2018-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多