C++. Пример создания шаблонного класса Матрица с динамическим выделением памяти




Пример создания шаблонного класса Матрица с динамическим выделением памяти

В примере демонстрируется создание шаблонного класса MATRIX. Класс реализует матрицу размером m*n. Память для матрицы выделяется динамически.

По данному примеру можно создавать и использовать собственные классы, которые содержат структуры данных в которых память выделяется динамически.

В классе реализованы:

  • скрытая (private) внутренняя переменная M типа «указатель на указатель». Эта переменная определяет матрицу. Память для матрицы будет выделяться динамически;
  • две целочисленные внутренние private-переменные m, n. Эти переменные определяют размерность матрицы M;
  • конструктор по умолчанию (без параметров);
  • конструктор с двумя параметрами MATRIX(int, int). Этот конструктор создает матрицу размером m*n. В конструкторе выделяется память для столбцов и строк матрицы. Значение каждого элемента матрицы устанавливается в 0;
  • конструктор копирования MATRIX(MATRIX&). Этот конструктор необходим для создания копии объекта-матрицы из другого объекта-матрицы;
  • методы чтения/записи элементов матрицы GetMij(), SetMij();
  • метод Print(), который выводит матрицу;
  • оператор копирования operator=(MATRIX&). Этот оператор перегружает оператор присваивания = и предназначен для корректного копирования объектов типа obj2=obj1;
  • деструктор.

Реализация класса для приложения типа Console Application имеет следующий вид

#include <iostream>
using namespace std;

// шаблонный класс Матрица
template <typename T>
class MATRIX
{
private:
  T** M; // матрица
  int m; // количество строк
  int n; // количество столбцов

public:
  // конструкторы
  MATRIX()
  {
    n = m = 0;
    M = nullptr; // необязательно
  }

  // конструктор с двумя параметрами
  MATRIX(int _m, int _n)
  {
    m = _m;
    n = _n;

    // Выделить память для матрицы
    // Выделить пам'ять для массива указателей
    M = (T**) new T*[m]; // количество строк, количество указателей

    // Выделить память для каждого указателя
    for (int i = 0; i < m; i++)
      M[i] = (T*)new T[n];

    // заполнить массив M нулями
    for (int i = 0; i < m; i++)
      for (int j = 0; j < n; j++)
        M[i][j] = 0;
  }

  // Конструктор копирования - обязательный
  MATRIX(const MATRIX& _M)
  {
    // Создается новый объект для которого виделяется память
    // Копирование данных *this <= _M
    m = _M.m;
    n = _M.n;

    // Выделить память для M
    M = (T**) new T*[m]; // количество строк, количество указателей

    for (int i = 0; i < m; i++)
      M[i] = (T*) new T[n];

    // заполнить значениями
    for (int i = 0; i < m; i++)
      for (int j = 0; j < n; j++)
        M[i][j] = _M.M[i][j];
  }

  // методы доступа
  T GetMij(int i, int j)
  {
    if ((m > 0) && (n > 0))
      return M[i][j];
    else
      return 0;
  }

  void SetMij(int i, int j, T value)
  {
    if ((i < 0) || (i >= m))
      return;
    if ((j < 0) || (j >= n))
      return;
    M[i][j] = value;
  }

  // метод, выводящий матрицу
  void Print(const char* ObjName)
  {
    cout << "Object: " << ObjName << endl;
    for (int i = 0; i < m; i++)
    {
      for (int j = 0; j < n; j++)
        cout << M[i][j] << "\t";
      cout << endl;
    }
    cout << "---------------------" << endl << endl;
  }

  // оператор копирования - обязательный
  MATRIX operator=(const MATRIX& _M)
  {
    if (n > 0)
    {
      // освободить память, выделенную ранее для объекта *this
      for (int i = 0; i < m; i++)
        delete[] M[i];
    }

    if (m > 0)
    {
      delete[] M;
    }

    // Копирование данных M <= _M
    m = _M.m;
    n = _M.n;

    // Выделить память для M опять
    M = (T**) new T*[m]; // количество строк, количество указателей
    for (int i = 0; i < m; i++)
      M[i] = (T*) new T[n];

    // заполнить значениями
    for (int i = 0; i < m; i++)
      for (int j = 0; j < n; j++)
        M[i][j] = _M.M[i][j];
    return *this;
  }

  // Деструктор - освобождает память, выделенную для матрицы
  ~MATRIX()
  {
    if (n > 0)
    {
      // освободить выделенную память для каждой строки
      for (int i = 0; i < m; i++)
        delete[] M[i];
    }

    if (m > 0)
      delete[] M;
  }
};

void main()
{
  // тест для класса MATRIX
  MATRIX<int> M(2, 3);
  M.Print("M");

  // Заполнить матрицу значеннями по формуле
  int i, j;
  for (i = 0; i < 2; i++)
    for (j = 0; j < 3; j++)
      M.SetMij(i, j, i + j);

  M.Print("M");

  MATRIX<int> M2 = M; // вызов конструктора копирования
  M2.Print("M2");

  MATRIX<int> M3; // вызов оператора копирования - проверка
  M3 = M;
  M3.Print("M3");

  MATRIX<int> M4;
  M4 = M3 = M2 = M; // вызов оператора копирования в виде "цепочки"
  M4.Print("M4");
}





Результат выполнения программы

Object: M
0       0       0
0       0       0
---------------------

Object: M
0       1       2
1       2       3
---------------------

Object: M2
0       1       2
1       2      3
---------------------

Object: M3
0       1       2
1       2       3
---------------------

Object: M4
0       1       2
1       2       3
---------------------

Итог. Если память в классе выделяется динамически, то обязательно нужно реализовывать собственный конструктор копирования и оператор копирования.

 


Связанные темы