C++. Оператори 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 звільняється.

⇑


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

⇑