Динамические объекты данных

Одномерные динамические массивы

С помощью указателей можно создавать динамические массивы — это массивы, память для которых выделяется не на этапе компиляции, а во время исполнения программы. Это очень удобно. Ведь программист далеко не всегда может заранее представить, какого размера должен быть тот или иной массив. Указывать размер массива с большим запасом (на всякий случай!) не разумно. Проще на этапе исполнения предложить пользователю задать самому необходимое количество элементов массива, либо требуемый размер массива должен вычисляться в программе по каким-то формулам.

Для динамического выделения памяти служит операция new, освобождение ставшей ненужной памяти выполняется операцией delete.

Приведём формальную запись оператора, с помощью которого выделяется память под динамический массив:

Указатель = new Тип [Количество_элементов];

где Тип — это тот же тип данных, что и тип, на который рассчитан указатель.

Запись оператора для освобождения динамической памяти ещё проще:

delete [] Указатель;

Здесь пустые квадратные скобки — это признак того, что освобождается память, отводимая для массива, а не для одиночного объекта.

Пример. Дан массив целых чисел из n элементов. Инвертировать этот массив, т.е. поменять местами первый элемент и последний, второй — и предпоследний и так далее. Память под массив выделять динамически.

Возможный текст программы:

#include <iostream>

using namespace std;

int main()

{

   int i, j, n;

   int *x;

   int c;


   // Задаём размер будущего массива

   do {

      cout << "n=";

      cin >> n;

   } while(n < 1);


   // Динамически выделяем память под массив

   x = new int[n];


   // Ввод массива с клавиатуры

   cout << "Input " << n << " numbers:" << endl;

   for(i = 0; i < n; i++)

      cin >> x[i];


   // Перестановка элементов массива

   for(i = 0, j = n - 1; i < j; i++, j--)

   {

      c = x[i];

      x[i] = x[j];

      x[j] = c;

   }


   // Вывод массива на экран монитора

   cout << "Otvet:" << endl;

   for(i = 0; i < n; i++)

      cout << x[i] << endl;


   // Освобождение динамической памяти, отводимой под массив

   delete [] x;


   return 0;

}

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

Важно не забывать освобождать память, занимаемую динамическими объектами. Иначе могут начаться проблемы в работе компьютера в целом. Неосвобождённые программой участки памяти могут оказаться недоступны после завершения работы вашей программы. Это явление называют «утечка памяти». Конечно, в современных операционных системах всегда реализуется «сборка мусора», т.е. есть некая служебная программа, которая наводит порядок, отыскивает «бесхозные» участки памяти и возвращает их в общее пользование. Но не надо излишне полагаться на «дядю», т.к. любые проблемы операционной системы из-за неудачной оптимизации ОС, из-за вирусов и тому подобного, могут отразиться на работе «сборщика мусора». В этом случае именно ваша программа будет регулярно «подвешивать» систему.

Одиночные динамические объекты

Выделять память динамически можно не только под массивы, но и под одиночные объекты. Конечно, если одиночный объект имеет стандартный тип (double, int и т.д.), то проще использовать обычные переменные. При работе с пользовательскими типами (структуры, классы) в ряде случаев есть смысл в динамическом выделении памяти под одиночные объекты, а для построения динамических структур типа списки, очереди, деревья и т.д. это просто необходимо.

Пусть имеется некий класс (или структура) Alfa (о классах и структурах речь пойдёт позже. Сейчас можно относится к ним просто как к типам данных). Тогда можно сделать следующее:

а)Создаём указатель x на объект типа Alfa:

Alfa *x;

б)Выделяем память под динамический объект:

x = new Alfa;

в)Далее работаем с объектом класса через указатель x. Как это делается — будем разбираться позже, при изучении структур и классов.

г)Как только объект стал ненужным — освобождаем занимаемую им память:

delete x;

В общем, всё делается по аналогии с динамическими массивами, но только ещё проще. Не надо указывать количество элементов при выделении памяти, не нужны квадратные скобки при её освобождении.

Двумерные динамические массивы

Мы разобрались с тем, как динамически выделять память под одиночные объекты и одномерные массивы. Теперь настало время решить эту же проблему и для матриц.

Как мы уже знаем из темы «Матрицы», матрица — это массив из одномерных массивов, каждый из которых представляет одну строку матрицы. Напрашивается идея: завести для каждой строки матрицы указатель и динамически выделить память под заданное количество элементов. Так как строк в матрице может быть достаточно много, а указатели, используемые для выделения памяти под каждую строку матрицы, имеют один и тот же тип, то эти указатели есть смысл объединить в одномерный массив указателей. Естественно, память под массив указателей также будем выделять динамически. А адрес этого массива указателей будем хранить в указателе на указатель. На рисунке покажем, как это может выглядеть:


Здесь

Тип ** имя_указателя;

Порядок работы с динамической матрицей рассмотрим на примере.

Пример. В прямоугольной матрице подсчитать количество отрицательных элементов. Память под матрицу выделять динамически.

Возможный текст программы:

#include <iostream>

using namespace std;

int main()

{

   int n = 3; // число строк

   int m = 4; // число столбцов

   double **a; // указатель на указатель на тип double

   int i, j;


   // Динамическое выделение памяти под матрицу:

   // ------------------------------------------

   // 1)создаём массив указателей типа указатель на double

   a = new double* [n];


   // 2)теперь выделяем память (построчно) собственно под матрицу, т.е. под данные

   for(i = 0; i < n; i++)

      a[i] = new double[m];

   //-------------------------------------------


   // Ввод матрицы с клавиатуры

   cout << "Matriza A("<< n << "*" << m << "):" << endl;

   for(i = 0; i < n; i++)

      for(j = 0; j < m; j++)

         cin >> a[i][j];


   // Подсчёт количества отрицательных чисел в матрице

   int k = 0;

   for(i = 0; i < n; i++)

      for(j = 0; j < m; j++)

         if(a[i][j] < 0)

            k++;

   cout << "k=" << k << endl;


   // Освобождение динамической памяти:

   // ---------------------------------

   // 1)вначале освободим память, отведённую собственно под матрицу, т.е. под данные

   for(i = 0; i < n; i++)

      delete []a[i];


   // 2)теперь освобождаем память, занятую массивом указателей

   delete []a;

   // ---------------------------------


   return 0;

}

Как видим, вначале выделяется память под массив указателей, затем под данные. Освобождение памяти делается в обратном порядке: вначале освобождают память, занятую данными матрицы, а затем — массивом указателей.

Когда память выделена, действия с элементами динамической матрицы выполняются точно так же, как и с обычной матрицей, т.е. везде, где это необходимо, используем обычную форму обращения к элементу матрицы a[i][j].

Hosted by uCoz
Назад
На верх
Вперёд
Hosted by uCoz