Оператори new і delete[]. Виділення пам’яті для структурних змінних, об’єктів класів, масивів. Ініціалізація виділеної пам’яті. Приклад перерозподілу раніше виділеної пам’яті

Оператори new і delete[]. Виділення пам’яті для структурних змінних, об’єктів класів, масивів. Ініціалізація виділеної пам’яті. Приклад перерозподілу раніше виділеної пам’яті


Зміст



1. Приклад динамічного виділення пам‘яті для структурної змінної

Виділення та звільнення пам‘яті для структурної змінної. Нехай задано структуру Date, яка має наступний опис:

// структура, що описує день року
struct Date
{
    int day;
    int month;
    int year;
};

Тоді, щоб виділити та використати пам’ять для змінної типу struct Date потрібно написати приблизно наступний код:

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

// структура, що описує день року
struct Date
{
    int day;
    int month;
    int year;
};

int main()
{
    // виділення пам'яті для структурної змінної оператором new
    struct Date * pd; // покажчик на struct Date

    try
    {
        pd = new struct Date; // спроба виділити пам'ять
    }
    catch (bad_alloc ba)
    {
        cout << "Пам\'ять не виділено" << endl;
        return -1;
    }

    // якщо пам'ять виділено, то
    // використання pd у програмі
    // встановити дату 11.02.2029
    pd->day = 11;
    pd->month = 2;
    pd->year = 2029;

    cout << pd->month << endl;

    // звільнити пам'ять використану під змінну pd
    delete pd;

    return 0;
}

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

У прикладі динамічно виділяється пам’ять для покажчика на об’єкт класу CDayWeek. Приклад реалізований для додатків типу Console Application.

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

// клас, що описує день тижня
class CDayWeek
{
    int d;

    public:
    // конструктор
    CDayWeek() { d = 1; }

    // методи доступу
    int Get(void) { return d; }
    void Set(int nd)
    {
        if ((1<=nd)&&(nd<=7))
            d = nd;
    }
};

int main()
{
    // виділення пам'яті для об'єкту класу оператором new
    CDayWeek *p;

    try
    {
        // спроба виділити пам'ять для покажчика p
        p = new CDayWeek(); // викликається конструктор класу CDayWeek
    }
    catch (bad_alloc ba)
    {
        cout << "Пам\'ять не виділено" << endl;
        cout << ba.what() << endl;
        return -1;
    }

    // якщо пам'ять виділено, то використання класу
    p->Set(3);

    cout << p->Get() << endl;

    // звільнити пам'ять, на яку вказує покажчик p
    delete p;

    return 0;
}

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

Оператор new може бути використаний для виділення пам‘яті для масиву. Загальна форма оператора new у випадку виділення пам‘яті для масиву покажчиків:

ptrArray = new type[size];

де

  • ptrArray – ім‘я масиву, для якого виділяється пам‘ять;
  • type – тип елементів масиву. Тип елементів може бути базовий (int, float, …) або інший структура, клас тощо);
  • size – розмір масиву (кількість елементів).

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

Для виділення пам‘яті, виділеної під масив, оператор delete має наступну форму використання:

delete[] ptrArray;

де ptrArray – ім‘я масиву, для якого виділяється пам‘ять.

5. Приклад динамічного виділення та звільнення пам‘яті для масиву покажчиків на базовий тип

У прикладі виділяється пам‘ять для масиву покажчиків на тип float. Потім елементи масиву заповнюються довільними значеннями. Після цього, виділена пам‘ять звільняється оператором delete[].

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

ptrArray = new float[10]; // виділити пам'ять для 10 елементів типу float

// використання масиву
int d;
for (int i = 0; i < 10; i++)
    ptrArray[i] = i * 2 + 1;

d = ptrArray[3]; // d = 7

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

6. Приклад виділення пам‘яті для масиву структурних змінних та його використання

У прикладі демонструється виділення та звільнення пам’яті для масиву з 3-х структур типу TStudent. Також продемонстровано способи доступу до полів заданого елементу в масиві структур.

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

// структура, що описує інформацію про студента
struct TStudent
{
    string name; // П.І.Б. студента
    string numBook; // номер залікової книжки
    int course; // курс навчання
    float rank; // рейтинг студента
};

int main()
{
    // виділення пам'яті для масиву структур
    int n_students = 3; // к-сть студентів
    TStudent * pS; // покажчик, масив структур

    try
    {
        // спроба виділити пам'ять для масиву структур
        pS = new struct TStudent[n_students];
    }
    catch (bad_alloc ba)
    {
        cout << "Пам\'ять не виділено" << endl;
        cout << ba.what() << endl;
        return -1;
    }

    // якщо пам'ять виділено, то використання масиву структур
    // доступ до елементу масиву з індексом 0
    pS->name = "Johnson J.";
    pS->numBook = "12389239";
    pS->rank = 3.93;
    pS->course = 4;

    // доступ до елементу масиву з індексом 1 - теж добре
    (pS + 1)->name = "Sullivan";
    (pS + 1)->numBook = "20930032";
    (pS + 1)->rank = 4.98;
    pS[1].course = 3;

    // доступ до елементу масиву з індексом 2 ще один спосіб
    pS[2].name = "Abram F.D.";
    pS[2].numBook = "l2l28983";
    pS[2].rank = 4.32;
    pS[2].course = 5;

    // вивід деяких значень
    cout << pS->name.c_str() << endl; // "Johnson J."
    cout << pS[1].rank << endl;       // 4.98
    cout << (pS + 2)->numBook.c_str() << endl;       // "12128983"

    // звільнити пам'ять, на яку вказує покажчик p
    delete[] pS;

    return 0;
}

7. Приклад виділення та звільнення пам‘яті для масиву об‘єктів. Ініціалізація масиву об’єктів

У прикладі демонструється виділення пам’яті для масиву об’єктів оператором new. Після використання масиву, відбувається знищення виділеної пам’яті оператором delete.

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

// клас, що описує місяць року
class CMonth
{
    private:
    int month;

    public:
    // конструктор без параметрів, якщо використовується масив об'єктів,
    // то цей конструктор є обов'язковим
    CMonth() { month = 1; }

    // конструктор з 1 параметром, для створення масиву об'єктів не підходить,
    // підходить тільки для одиночних об'єктів
    CMonth(int nmonth)
    {
        if ((1 <= nmonth) && (nmonth <= 12))
            month = nmonth;
        else
            month = 1;
    }

    // методи доступу
    int Get(void) { return month; }
    void Set(int nmonth)
    {
        if ((1 <= nmonth) && (nmonth <= 12))
            month = nmonth;
    }
};

int main()
{
    // виділення пам'яті для масиву об'єктів класу
    int nMonths = 12;
    CMonth * pM;

    try
    {
        // спроба виділити пам'ять для масиву з 12 об'єктів
        pM = new CMonth[12]; // для кожного елементу викликається конструктор без параметрів
    }
    catch (bad_alloc ba)
    {
        cout << "Пам\'ять не виділено" << endl;
        cout << ba.what() << endl;
        return -1;
    }

    // якщо пам'ять виділено, то заповнення масиву значеннями
    for (int i = 1; i <= 12; i++)
        pM[i - 1].Set(i);

    // вивід деяких значень
    cout << pM[3].Get() << endl; // 4
    cout << pM[5].Get() << endl; // 6

    // звільнити пам'ять, на яку вказує покажчик p
    delete[] pM;

    return 0;
}

У вищенаведеному коді, внутрішня змінна в масиві об’єктів ініціалізується значенням 1, тому що таке значення задано в конструкторі без параметрів CMonth()

...

// конструктор без параметрів є обов'язковим, тому що він виступає як ініціалізатор масиву
CMonth() { month = 1; }

...

Цей конструктор виступає ініціалізатором масиву. Однак, у класі реалізовано ще один конструктор – конструктор з 1 параметром або параметризований конструктор. Згідно синтаксису C++, масив не може бути ініціалізований параметризованим конструктором. Тому, в класі CMonth обов’язково має бути реалізований конструктор без параметрів.

Якщо конструктор без параметрів CMonth() забрати з коду класу, то неможливо буде виділити пам’ять для масиву об’єктів. Можна буде виділяти пам’ять для одиночних об’єктів, але не для масиву.

Висновок: якщо потрібно виділити пам’ять для масиву об’єктів деякого класу, то цей клас обов’язково має мати реалізацію конструктора без параметрів.

8. Як перерозподілити пам‘ять, якщо потрібно динамічно збільшити розмір масиву? Перерозподіл пам’яті для структур, ініціалізація структур. Приклад

У прикладі демонструється процес перерозподілу пам’яті для типу структури DayWeek. Виділення та перерозподіл пам’яті динамічно є основною перевагою цього способу у порівнянні зі статичним виділенням пам’яті. Пам’ять у програмі можна виділяти коли потрібно і скільки потрібно.

У структурі DayWeek реалізовано конструктор без параметрів (за замовчуванням), який ініціалізує масив структур значенням за замовчуванням.

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

// структура, що описує день тижня
struct DayWeek
{
    int day;

    // структура як і клас також може мати конструктор
    DayWeek() { day = 1; }
};

int main()
{
    // виділення пам'яті для масиву структур та ініціалізація
    DayWeek * p;

    try
    {
        // спроба виділити пам'ять для масиву з 5 структур типу DayWeek
        p = new DayWeek[5];
    }
    catch (bad_alloc ba)
    {
        cout << "Пам\'ять не виділено" << endl;
        cout << ba.what() << endl;
        return -1;
    }

    // якщо пам'ять виділено, то заповнення масиву значеннями
    for (int i = 0; i < 5; i++)
        p[i].day = i + 1;

    // використання масиву з 5 структур
    int d;
    d = (p + 1)->day; // d = 2 

    // Демонстрація збільшення (перерозподілу) пам'яті,
    // потрібно збільшити до 7 елементів в масиві (7 днів на тиждень)
    // 1. Оголосити покажчик на новий масив
    DayWeek * p2;

    try
    {
        // 2. Спробувати виділити пам'ять для нового масиву (для 7 елементів)
        p2 = new DayWeek[7];
    }
    catch (bad_alloc ba)
    {
        // якщо помилка, то завершення з кодом -1
        cout << "Пам\'ять не виділено" << endl;
        cout << ba.what() << endl;
        return -1;
    }

    // 3. Зберегти перші 5 елементів старого масиву (скопіювати p => p2)
    for (int i = 0; i < 5; i++)
        p2[i] = p[i];

    // 4. Звільнити пам'ять, що була виділена для старого масиву p
    delete[] p;

    // 5. Перенаправити покажчик з p на p2.
    p = p2; // Обидва покажчики вказують на одну й туж область пам'яті
 
    // 6. Використати масив з 7 елементів, наприклад, дозаповнювати 2 останні
    for (int i = 5; i < 7; i++)
        p[i].day = i + 1;

    d = p[5].day; // d = 6
    d = (p + 6)->day; // d = 7

    // 7.Після завершення роботи, звільнити пам'ять для покажчика p
    delete[] p; // можна було delete[] p2; оскільки p і p2 вказують на ту саму область пам'яті

    return 0;
}

У функції main() спочатку виділяється пам’ять для масиву з 5 структур. Потім ця пам’ять перерозподіляється для масиву з 7 структур. Для цього використовується додатковий покажчик p2.

При перерозподілі спочатку пам’ять виділяється для p2 (7 елементів). Потім копіюються дані з p у p2. Після цього звільняється пам’ять, що була виділена для покажчика p (5 елементів).

Наступним кроком значення p встановлюється рівним значенню p2. Таким чином, обидва покажчики вказують на одну і ту саму ділянку пам’яті.

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


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