Динамічне виділення пам’яті. Оператор new. Виділення пам’яті для типів-значень, структур, перечислень, об’єктів класів. Виділення пам’яті для масивів

Динамічне виділення пам’яті. Оператор new. Виділення пам’яті для типів-значень, структур, перечислень, об’єктів класів. Виділення пам’яті для масивів


Зміст



1. Як в C# реалізується динамічне виділення пам’яті? Призначення оператора new. Загальна форма

У програмах на C# пам’ять для об’єктів потрібно виділяти динамічно. Динамічне виділення пам’яті для об’єктів чи інших видів даних реалізується з допомогою оператора new. Загальна форма оператора new

new ClassName(parameters_list)

де

  • ClassName – ім’я класу, для об’єкту якого виділяється пам’ять;
  • parameter_list – список параметрів, які приймає один з конструкторів класу. Обов’язково, у класі має бути реалізований конструктор, параметри якого співпадають з parameter_list. Конструктор може бути без параметрів (конструктор за замовчуванням).

 

2. Виділення пам’яті оператором new для масиву об’єктів. Загальна форма

Загальна форма виділення пам’яті для масиву об’єктів, що є екземплярами класу ClassName, має вигляд:

new ClassName[size]

де

  • ClassName – ім’я класу, який є базовим типом для масиву об’єктів (екземплярів);
  • size – розмірність масиву.

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

Object reference not set to an instance of an object
Посилання на об’єкт не встановлене на екземпляр об’єкту

 

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

Нехай задано клас, що описує загальну інформацію про країну. Оголошення класу має такий вигляд:

// клас, що містить загальну інформацію про країну
class Country
{
    string name; // назва країни
    ulong population; // населення
    double square; // площа
    string capital; // столиця

    // конструктори класу
    // конструктор без параметрів
    public Country()
    {
        name = "";
        population = 0;
        square = 0.0;
        capital = "";
    }

    // конструктор з 4 параметрами
    public Country(string _name, ulong _population, double _square, string _capital)
    {
        name = _name;
        population = _population;
        square = _square;
        capital = _capital;
    }

    // методи доступу
    public void Set(string _name, ulong _population, double _square, string _capital)
    {
        name = _name;
        population = _population;
        square = _square;
        capital = _capital;
    }

    public void Get(out string _name, out ulong _population, out double _square, out string _capital)
    {
        _name = name;
        _population = population;
        _square = square;
        _capital = capital;
    }
}

Тоді виділення пам’яті для класу та його використання може бути таким

// використання класу Country

// виділення пам'яті - викликається конструктор без параметрів
Country c1 = new Country();

// виділення пам'яті з ініціалізацією конструктором з 4 параметрами
Country c2;
c2 = new Country("Namibia", 2358163, 825418, "Windhoek");

// використання методів класу c2
string name, cap;
double square;
ulong popul;

c2.Get(out name, out popul, out square, out cap);
// name = "Namibia", popul = 2358163, square = 825418, cap = "Windhoek"

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

 

4. Приклад виділення пам’яті для масиву об’єктів (екземплярів) класу

Пам’ять для масиву об’єктів деякого класу виділяється в 2 етапи. Спочатку виділяється пам’ять для масиву посилань на екземпляри класу. Потім, в циклі, виділяється пам’ять для кожного об’єкту (екземпляру) класу.

Приклад. Нехай задано клас MyClass.

// клас MyClass
class MyClass
{
    public int d; // внутрішня змінна класу
}

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

...

MyClass[] mcA = new MyClass[10]; // виділення пам'яті для масиву в цілому

// виділення пам'яті для кожного об'єкту
for (int i = 0; i < 10; i++)
    mcA[i] = new MyClass();

// заповнення масиву mcA квадратами чисел від 0 до 9
for (int i = 0; i < 10; i++)
    mcA[i].d = i * i;

...

 

5. Приклад виділення пам’яті для структури

Нехай задано структуру Date, що реалізує дату

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

    // конструктор структури
    public Date(int day, int month, int year)
    {
        this.day = day;
        this.month = month;
        this.year = year;
    }

    // метод, що повертає значення полів day, month, year
    public void Get(out int d, out int m, out int y)
    {
        d = day;
        m = month;
        y = year;
    }
}

Виділення пам’яті для структурної змінної з іменем dt та використання структурної змінної має вигляд:

Date dt = new Date(11, 05, 2008); // виділення пам'яті для структурної змінної

int d, m, y;
dt.Get(out d, out m, out y); // взяти день тижня: d=11, m=5, y=2008

 

6. Приклад виділення пам’яті для масиву структур

Виділення пам’яті для масиву структур реалізується в два етапи. Спочатку виділяється пам’ять для масиву змінних (посилань). Потім виділяється пам’ять для кожного елементу масиву (кожної структурної змінної).

Нехай задано структуру Worker, що реалізує дані про працівника:

// структура, що описує дані про працівника
struct Worker
{
    public string name; // Прізвище та ім'я працівника
    public int age; // вік працівника
    public float rank; // рейтинг працівника
}

Демонстрація використання масиву структур Worker:

Worker[] W; // оголошення змінної W типу масив структур Worker

W = new Worker[20]; // виділення пам'яті для масиву посилань (об'єктів) на структури

// Виділення пам'яті для кожного об'єкту структури - обов'язково
for (int i = 0; i < W.Length; i++)
    W[i] = new Worker();

// Заповнення масиву W значеннями
for (int i=0; i<W.Length; i++)
{
    W[i].name = "";
    W[i].age = 100;
    W[i].rank = 1.0f;
}

 

7. Приклад виділення пам’яті для перечислення

Задано перечислення DayWeek

// перечислення DayWeek
enum DayWeek
{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}

Виділення пам’яті для змінної типу перечислення DayWeek та її використання може бути таким:

DayWeek dw = new DayWeek();
dw = DayWeek.Monday;
int d = (int)dw; // d = 0

dw = DayWeek.Thursday;
d = (int)dw; // d = 3

 

8. Виділення пам’яті для типів-значень. Приклади

Оператор new може виділяти пам’ять для типів значень, таких як int, double і т.д. У цьому випадку викликається конструктор, що ініціалізує змінну нульовим значенням. Виклик оператора new для будь-якого типу-значення призводить до виклику конструктора за замовчуванням для даного типу.

Приклад 1. Виділення пам’яті для одиночних типів-значень.

// виділення пам'яті для типів-значень
int i = new int();
double d = new double();
bool b = new bool();

i = 23;
d = 7.323;
b = false;

Приклад 2. Виділення пам’яті для масивів типів-значень.
У прикладі виділяється пам’ять для масиву з 20 чисел типу int та масиву з 30 чисел типу double.

// виділення пам'яті для масивів типів-значень
int[] A = new int[20]; // масив з 20 цілих чисел
double[] B = new double[30]; // масив з 30 чисел типу double

// заповнення масивів значеннями
for (int j = 0; j < A.Length; j++)
    A[j] = j * j + 2;

for (int j = 0; j < B.Length; j++)
    B[j] = 0.5 * j + 1;

Як видно з прикладу, не потрібно виділяти пам’ять для кожного елементу масиву як це робиться у випадку з масивами об’єктів. Однак, за бажанням можна виділити як показано нижче.

// виділення пам'яті для кожного елементу масиву A
for (int j = 0; j < A.Length; j++)
    A[j] = new int();

 


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