C++. Динамічне та статичне виділення пам’яті. Переваги та недоліки. Виділення пам‘яті для одиночних змінних операторами new і delete. Можливі критичні ситуації при виділенні пам’яті. Ініціалізація при виділенні пам’яті




Динамічне та статичне виділення пам’яті. Переваги та недоліки. Виділення пам‘яті для одиночних змінних операторами new і delete. Можливі критичні ситуації при виділенні пам’яті. Ініціалізація при виділенні пам’яті


Зміст


Пошук на інших ресурсах:

1. Динамічне та статичне (фіксоване) виділення пам‘яті. Основні відмінності

Для роботи з масивами інформації програми мають виділяти пам‘ять для цих масивів. Для виділення пам‘яті під масиви змінних використовуються відповідні оператори, функції тощо. У мові програмування C++ виділяють такі види виділення пам‘яті:

1. Статичне (фіксоване) виділення пам‘яті. У цьому випадку пам‘ять виділяється тільки один раз під час компіляції. Розмір виділеної пам‘яті є фіксований і незмінний до кінця виконання програми. Прикладом такого виділення може служити оголошення масиву з 10 цілих чисел:

int M[10]; // пам‘ять для масиву виділяється один раз, розмір пам‘яті фіксований

2. Динамічне виділення пам‘яті. У цьому випадку використовується комбінація операторів new і delete. Оператор new виділяє пам‘ять для змінної (масиву) в спеціальній області пам‘яті, яка називається “купа” (heap). Оператор delete звільняє виділену пам‘ять. Кожному оператору new має відповідати свій оператор delete.

⇑

2. Переваги та недоліки використання динамічного та статичного способів виділення пам‘яті

Динамічне виділення пам‘яті порівняно зі статичним виділенням пам‘яті дає такі переваги:

  • пам‘ять виділяється в міру необхідності програмним шляхом;
  • немає зайвих витрат невикористаної пам‘яті. Виділяється стільки пам‘яті скільки потрібно і коли потрібно;
  • можна виділяти пам‘ять для масивів інформації, розмір яких наперед невідомий. Визначення розміру масиву формується у процесі виконання програми;
  • зручно здійснювати перерозподіл пам‘яті. Або іншими словами, зручно виділяти новий фрагмент для того самого масиву, якщо потрібно виділити додаткову пам‘ять або звільнити непотрібну;
  • при статичному способі виділення пам‘яті важко перерозподіляти пам‘ять для змінної-масиву, оскільки вона вже виділена фіксовано. У випадку динамічного виділення, це здійснюється легко і зручно.

Переваги статичного способу виділення пам‘яті:

  • статичне (фіксоване) виділення пам‘яті краще використовувати, коли розмір масиву інформації наперед відомий і є незмінний протягом виконання усієї програми;
  • статичне виділення пам‘яті не потребує додаткових операцій звільнення з допомогою оператора delete. Звідси випливає зменшення помилок програмування. Кожному оператору new має відповідати оператор delete;
  • природність (натуральність) представлення програмного коду, що оперує статичними масивами.

В залежності від поставленої задачі, програміст має вміти правильно визначити, який спосіб виділення пам‘яті підходить для тієї чи іншої змінної (масиву).

⇑

3. Як виділити пам‘ять оператором new для одиночної змінної? Загальна форма

Загальна форма виділення пам‘яті для одиночної змінної оператором new має вигляд:

ptrName = new type;

де

  • ptrName – ім‘я змінної (покажчика), яка буде вказувати на виділену пам‘ять;
  • type – тип змінної. Розмір пам‘яті виділяється достатній для поміщення в неї значення змінної даного типу type.

⇑

4. Як звільнити пам‘ять, виділену під одиночну змінну оператором delete? Загальна форма

Якщо пам‘ять для змінної виділена оператором new, то після завершення використання змінної, цю пам‘ять потрібно звільнити оператором delete. У мові C++ це є обов‘язковою умовою. Якщо не звільнити пам‘ять, то пам‘ять залишиться виділеною (зайнятою), але використовувати її не зможе жодна програма. У даному випадку відбудеться “витік пам‘яті” (memory leak).

У мовах програмування Java, C# звільняти пам‘ять після виділення не потрібно. Цим займається “збирач сміття” (garbage collector).

Загальна форма оператора delete для одиночної змінної:

delete ptrName;

де ptrName – ім‘я покажчика, для якого було раніше виділено пам‘ять оператором new. Після виконання оператора delete покажчик ptrName вказує на довільний фрагмент пам‘яті, який не є зарезервований (виділений).

⇑

5. Приклади виділення (new) та звільнення (delete) пам‘яті для покажчиків базових типів

У прикладах демонструється використання операторів new і delete. Приклади мають спрощений вигляд.

Приклад 1. Покажчик на тип int. Найпростіший приклад

// виділення пам'яті оператором new
int * p; // покажчик на int

p = new int; // виділити пам'ять для покажчика
*p = 25; // занести значення в пам'ять

// використання пам'яті, виділеної для покажчика
int d;

d = *p; // d = 25

// знищити пам'ять, виділену для покажчика - обов'язково
delete p;

Приклад 2. Покажчик на тип double

// виділення пам'яті для покажчика на double
double * pd = nullptr;

pd = new double; // виділити пам'ять

if (pd != nullptr)
{
    *pd = 10.89; // занести значення
    double d = *pd; // d = 10.89 - використання у програмі
    delete pd; // звільнити пам'ять
}

⇑

6. Що таке “витік пам‘яті” (memory leak)?

“Витік пам‘яті” – це коли пам‘ять для змінної виділяється оператором new, а по закінченні роботи програми її не звільнено оператором delete. У цьому випадку пам‘ять в системі залишається зайнятою, хоча потреби в її використанні вже немає, оскільки програма, що її використовувала, вже давно завершила свою роботу.

“Витік пам‘яті” є типовою помилкою програміста. Якщо “витік пам‘яті” повторюється багатократно, то можлива ситуація, коли буде “зайнята” вся доступна пам‘ять в комп‘ютері. Це призведе до непередбачуваних наслідків роботи операційної системи.






⇑

7. Яким чином виділити пам‘ять оператором new з перехопленням критичної ситуації, при який пам‘ять може не виділитись? Виключна ситуація bad_alloc. Приклад

При використанні оператора new можлива ситуація, коли пам‘ять не виділиться. Пам‘ять може не виділитись у таких ситуаціях:

  • якщо відсутня вільна пам‘ять;
  • розмір вільної пам‘яті менши за той, що був заданий в операторі new.

У цьому випадку генерується виключна ситуація bad_alloc. Програма має перехватити цю ситуацію та відповідним чином обробити її.

Приклад. У прикладі враховується ситуація, коли пам‘ять може не виділитись оператором new. У такому випадку здійснюється спроба виділити пам’ять. Якщо спроба вдала, то робота програми продовжується. Якщо спроба завершилася невдачею, то відбувається вихід з функції з кодом -1.

int main()
{
    // оголосити масив покажчиків на float
    float * ptrArray;

    try
    {
        // спробувати виділити пам'ять для 10 елементів типу float
        ptrArray = new float[10];
    }
    catch (bad_alloc ba)
    {
        cout << "Виключна ситуація. Пам\'ять не виділена" << endl;
        cout << ba.what() << endl;
        return -1; // вихід з функції
    }

    // якщо все добре, то використання масиву
    for (int i = 0; i < 10; i++)
        ptrArray[i] = i * i + 3;

    int d = ptrArray[5];
    cout << d << endl;

    delete[] ptrArray; // знищити пам'ять, виділену під масив
    return 0;
}

⇑

8. Виділення пам‘яті для змінної з одночасною ініціалізацією. Загальна форма. Приклад

Оператор виділення пам‘яті new для одиночної змінної допускає одночасну ініціалізацію значенням цієї змінної.

В загальному, виділення пам‘яті для змінної з одночасною ініціалізацією має вигляд

ptrName = new type(value)

де

  • ptrName – ім‘я змінної-покажчика, для якої виділяється пам‘ять;
  • type – тип на який вказує покажчик ptrName;
  • value – значення, яке встановлюється для виділеної ділянки пам’яті (значення за покажчиком).

Приклад. Виділення пам’яті для змінних з одночасною ініціалізацією. Нижче наводиться функція main() для консольного додатку. Продемонстровано виділення пам’яті з одночасною ініціалізацією. Також враховується ситуація, якщо спроба виділити пам’ять завершується невдачею (критична ситуація bad_alloc).

#include "stdafx.h"
#include <iostream>
using namespace std;

int main()
{
    // виділення пам'яті з одночасною ініціалізацією
    float * pF;
    int * pI;
    char * pC;

    try
    {
        // спробувати виділити пам'ять для змінних з одночасною ініціалізацією
        pF = new float(3.88); // *pF = 3.88
        pI = new int(250); // *pI = 250
        pC = new char('M'); // *pC = 'M'
    }
    catch (bad_alloc ba)
    {
        cout << "Виключна ситуація. Пам\'ять не виділена" << endl;
        cout << ba.what() << endl;
        return -1; // вихід з функції
    }

    // якщо пам'ять виділена, то використання покажчиків pF, pI, pC
    float f = *pF; // f = 3.88
    int i = *pI; // i = 250;
    char c;
    c = *pC; // c = 'M'

    // вивести ініціалізовані значення
    cout << "*pF = " << f<< endl;
    cout << "*pI = " << i << endl;
    cout << "*pC = " << c << endl;

    // звільнити пам'ять, виділену раніше для покажчиків
    delete pF;
    delete pI;
    delete pC;

    return 0;
}

⇑


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

⇑