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
---------------------

Підсумок. Якщо пам’ять в класі виділяється динамічно, то обов’язково потрібно реалізовувати власний конструктор копіювання та оператор копіювання.

 


Зв’язані теми