Выделение динамического 2D-массива с помощью шаблона - PullRequest
2 голосов
/ 08 июля 2011

В приложении 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));

Пожалуйста, помогите!


EDIT:

Хорошо, я забыл дать некоторую информацию. Пожалуйста, смотрите ниже:

У меня есть дополнительный метод, называемый "T Dot (Matrix)". У меня также есть два метода, называемых "Columns ()" и "Rows ()", которые являются просто получателями для _cols и _rows. И метод с именем «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 ()".

Ответы [ 3 ]

3 голосов
/ 08 июля 2011

Не вызывайте деструктор явно, вы выделили myMatrix в стеке, поэтому dtor будет автоматически вызываться, когда переменная выходит из области видимости, то есть когда main() возвращает.

2 голосов
/ 08 июля 2011

С вашим редактированием проблема здесь все еще заключается в отсутствии конструктора копирования и оператора присваивания (см. Мой ответ на ваш последний вопрос). Проблема в том, что когда вы передаете Matrix по значению в функцию, конструктор копирования вызывается для создания копии, но, поскольку вы не определили один C ++, будет использоваться конструктор копирования по умолчанию, который просто делает поверхностную копию. Следовательно, вы получите новый Matrix, который имеет указатель на те же элементы, что и старый Matrix. Когда этот новый Matrix выходит из области видимости, его деструктор срабатывает, очищая массив, используемый другим Matrix. Когда исходный Matrix выходит из области видимости и очищается, он пытается удалить уже удаленный массив, вызывая сбой.

Чтобы исправить это, вам нужно реализовать конструктор копирования и оператор присваивания, которые правильно дублируют ресурсы. Существует эмпирическое правило под названием «Правило трех», которое гласит, что если у вас есть деструктор, вам также понадобится конструктор копирования и оператор присваивания, чтобы предотвратить подобные ошибки. Попробуйте реализовать эти отсутствующие функции и посмотрите, устранена ли проблема.

1 голос
/ 08 июля 2011

Редактировать

Вот ошибка:

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

Эта функция должна возвращать T, и она не возвращается в конце !! Поставил,

return value;

И это должно быть решено. Также есть несколько предложений: Вы передаете Matrix<T> m; по значению, которое подвержено ошибкам. Потому что и скопированный m, и оригинальный intM2 будут указывать на один и тот же _matrix. Поэтому, когда m выйдет из области видимости, он будет delete[] всем. Когда intM2 выходит из области видимости, снова удаляет ту же самую память . Что опять крушение.

Таким образом, вы всегда должны указывать конструктор копирования (либо как private, либо с правильным кодом копирования). На данный момент измените определение на

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

Это разрешит все ваши ошибки. (Вы можете также пройти мимо const Matrix<T> &m, для этого вам, возможно, придется изменить GetValue () const.)

...