Структури. Частина 3. Робота з managed-структурами в середовищі CLR. Кваліфікатори ref і value. Оголошення структурних змінних. Масиви managed-структурних змінних. Ініціалізація managed-структур

Структури. Частина 3. Робота з managed-структурами в середовищі CLR. Кваліфікатори ref і value. Оголошення структурних змінних. Масиви managed-структурних змінних. Ініціалізація managed-структур

Ця тема базується на використанні теми:

Структури. Частина 1. Складені типи даних. Структурна змінна. Структури в середовищі CLR. Оголошення та ініціалізація структурної змінної


Зміст


1. Які кваліфікатори використовуються з managed-структурами в середовищі CLR?

В середовищі CLR можна використовувати три види структур:

  • native-структури. Це класичні структури C/C++, які використовуються в попередніх версіях різних середовищ програмування. Такі структури не містять кваліфікатора;
  • managed-структури. Це структури, призначені для роботи в керованій пам’яті, що використовується в середовищі CLR. У цьому випадку значення покажчиків не може виходити за межі виділеної програмі пам’яті.

З managed-структурами у Visual C++/CLR використовуються два кваліфікатори (ключові слова):

  • кваліфікатор (ключове слово) ref. Цей кваліфікатор означає, що структура є типом-посиланням;
  • кваліфікатор value. Цей кваліфікатор означає, що структура є типом-значення.

2. Яка відмінність між кваліфікаторами ref і value при роботі зі структурами?

Якщо оголосити екземпляр структури з кваліфікатором ref чи value, то відмінностей немає. Доступ до елементів структур здійснюється через символ ‘ . ‘ (крапка).

Відмінність між кваліфікаторами ref і value проявляється у випадках:

  • якщо використовуються масиви структурних змінних (див. п.п. 5, 6);
  • якщо використовуються покажчики на структури.

При виділенні пам’яті для покажчика на структуру з модифікатором ref ця структура розміщується в керованій (managed) кучі (heap) і не може бути розміщена в native-кучі.

Якщо ж виділити пам’ять для покажчика на структуру з модифікатором value, то структура може бути розміщена і в managed-кучі і в native-кучі.

Детально про роботу покажчиків на managed-структури висвітлено у темі:

3. Як здійснюється робота зі структурами посилального типу (ref)? Приклад

У даному прикладі оголошено структуру, що описує точку на координатній площині. Доцільно розміщувати оголошення шаблону структури в окремому “*.h” файлі, наприклад “MyStruct.h”.

// структура MyPointR з кваліфікатором ref
ref struct MyPointR
{
    int x;
    int y;
};

Оголошення змінної типу MyPointR та її використання з іншого програмного коду (наприклад, обробника події кліку на кнопці):

MyPointR mp;
mp.x = 23;
mp.y = 36;

4. Як здійснюється робота зі структурами типу “значення” (value)? Приклад

Приклад оголошення шаблону value-структури та її екземпляру (змінної) в програмі.

// шаблон структури MyPointV з кваліфікатором value
value struct MyPointV
{
    int x;
    int y;
};
...
MyPointV mpv;
mpv.x = 334;
mpv.y = 43;


5. Приклад використання масиву структурних змінних з кваліфікатором ref. Масив ref-структур. Ключове слово array

Якщо структура оголошена з кваліфікатором ref (посилального типу), то оголосити масив структурних змінних в середовищі Visual C++/CLR стандартним для C/C++ способом не вдасться. Тобто, наступний код видасть помилку:

// ref-структури - посилального типу
MyPointR MP[5]; // - помилка!

Для оголошення масиву з ref-структур використовується ключове слово array. Нижче продемонстровано роботу з структурою типу MyPointR (див. п. 4).

// ref-структури - посилального типу
// MyPointR MP[5]; // - помилка!
array <MyPointR ^> ^MP; // оголошення масиву ref-структур - тільки так

// array <MyPointR> MP2; - помилка, має бути символ ^

// виділення пам'яті для масиву покажчиків на структури
MP = gcnew array <MyPointR ^>(5);

// виділення пам'яті для кожного покажчика
for (int i=0; i<5; i++)
    MP[i] = gcnew MyPointR;

// заповнення значеннями
for (int i=0; i<5; i++)
{
    MP[i]->x = i*2;
    MP[i]->y = i*(i+1);
}

6. Приклад використання масиву структурних змінних з кваліфікатором value. Масив value-структур. Ключове слово array
// шаблон структури MyPointV з кваліфікатором value
value struct MyPointV
{
    int x;
    int y;
};

// value-структури - типу "значення"
// можна працювати зі значеннями
array <MyPointV> ^MPV; // оголошення масиву

//array <MyPointV> MPV2; - так не можна, помилка. Має бути символ ^

// виділення пам'яті для 5 структур
MPV = gcnew array <MyPointV>(5);

// заповнення значеннями
for (int i=0; i<5; i++)
{
    MPV[i].x = i*2;
    MPV[i].y = i*i;
}

7. Як представити рядок в якості поля в шаблоні структурної змінної?

Якщо структура оголошена з кваліфікатором ref або value, то оголосити рядок символів класичним способом

char s[10];

не вдасться.

Для того, щоб у шаблоні структурної змінної використовувати рядки символів, потрібно використати один з двох способів:

  • описати покажчик на тип char;
  • описати масив типу char з використанням ключового слова array. Потім виконати відповідне перетворення у тип, який зручно обробляти (наприклад, String).

Приклад. Нехай дано шаблон структури, яка описує координати (x, y) точки на площині. Крім того, кожна координата має мати пояснення (коментар) для кожної точки. У прикладі показано два варіанти представлення рядка символів (типу char). З метою демонстрації роботи оголошено 2 змінні-покажчики на char, які мають різні типи оголошення покажчиків.

// структура MyPointR
ref struct MyPointR
{
    int x;
    int y;
    char * sx;         // покажчик на тип char - рядок символів
    array <char> ^sy; // використання ключового слова array
};

Робота з структурою MyPointR продемонстрована нижче.

MyPointR mpr;

mpr.x = 23;
mpr.y = 35;
mpr.sx = new char[10]; // так можна, працює
mpr.sy = gcnew array <char>(10); // теж працює
strcpy(mpr.sx, "X");

mpr.sy[0] = 'Y';
mpr.sy[1] = '\0';

// перетворення з масиву array <char> в масив char[10]
char buffer[10]; // допоміжна змінна
for (int i=0; i<10; i++)
    buffer[i] = mpr.sy[i];

// перетворення з char[] в String для зручнішого використання
String ^s = gcnew String(buffer);

8. Як в ref-структурі або value-структурі представити масив чисел? Приклади

В managed-структурах представити масив чисел класичним способом на зразок

int D[10];
double F[5][10];

не вдасться.

Пам’ять для масивів потрібно виділяти динамічно. Тому, для роботи з масивами потрібно описувати покажчик на базовий тип. Більш детально про роботу покажчиків описано в темах:

Нижче наведено приклади організації роботи з масивами, що входять в ref-структуру та value-структуру.

Приклад 1. Представлення масиву з n цілих чисел в ref-структурі.

// Шаблон ref-структури, в якій реалізовано масив з n цілих чисел
// двома способами
ref struct IntArrayR
{
    int n; // кількість чисел в масиві
    int * A1; // некерований покажчик на масив int-чисел
    array <int> ^A2; // managed-покажчик на масив int-чисел
};

Використання шаблону структури IntArrayR

// Шаблон ref-структури IntArrayR
IntArrayR mA; // mA - структурна змінна

// у масиві має бути 5 чисел
mA.n = 5;
// виділення пам'яті для масиву чисел int,
// на який вказує некерований (*) покажчик
mA.A1 = new int[mA.n];

// виділення пам'яті
mA.A2 = gcnew array <int>(mA.n);

// заповнення масивів A1 і A2 значеннями
for (int i=0; i<mA.n; i++)
{
    mA.A1[i] = i*3+5;
    mA.A2[i] = i*i-2;
}

// відображення масивів в listBox1 та listBox2
listBox1->Items->Clear();
listBox2->Items->Clear();
for (int i=0; i<mA.n; i++)
{
    listBox1->Items->Add(mA.A1[i].ToString());
    listBox2->Items->Add(mA.A2[i].ToString());
}

Приклад 2. Представлення масиву з n дійсних чисел в value-структурі. Знаходження суми елементів масиву.

// Шаблон value-структури, що реалізує масив з n дійсних чисел
value struct FloatArrayV
{
    float n;   // кількість чисел в масиві
    float * A1; // некерований покажчик на масив float-чисел
    array <float> ^A2; // managed-покажчик на масив float-чисел
};

Робота з структурою FloatArrayV:

// Використання value-структури FloatArrayV
FloatArrayV fA;

// у структурі масив з 10 чисел
fA.n = 10;
fA.A1 = new float[fA.n]; // виділення пам'яті для одновимірного масиву A1
fA.A2 = gcnew array <float>(10); // виділення пам'яті для масиву A2

// заповнення значень масивів
for (int i=0; i<fA.n; i++)
{
    fA.A1[i] = System::Math::Sin(i) + System::Math::Cos(i);
    fA.A2[i] = i*System::Math::Sqrt(i);
}

// Знаходження сум елементів масивів
float sum1 = 0, sum2 = 0;
for (int i=0; i<fA.n; i++)
{
    sum1 += fA.A1[i];
    sum2 += fA.A2[i];
}

label1->Text = "sum(A1) = " + sum1.ToString();
label2->Text = "sum(A2) = " + sum2.ToString();

9. Як здійснюється присвоєння managed-структур? Приклад

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

Оскільки, структури з кваліфікатором ref є структурами-посиланнями, то присвоювати їх не можна.

Приклад. Присвоєння value-структур.

// Присвоєння value-структур
MyPoint_value mpv;

mpv.x = 300;
mpv.y = 500;
mpv.color = 5;

MyPoint_value mpv2;

mpv2 = mpv; // присвоєння структур

int d;
d = mpv2.x; // d = 300

10. Як здійснюється ініціалізація managed-структур? Приклад

Для managed-структур ініціалізацію можна робити тільки для структур-значень або value-структур. Для структур-посилань, оголошення яких починається з слова ref, ініціалізацію зробити не вдасться.

Приклад 1. Ініціалізація структури з кваліфікатором value. Нехай задано шаблон структури, що описує точку на екрані монітора.

// структура оголошена з кваліфікатором value
value struct MyPoint_value
{
    int x;
    int y;
    int color;
};

Тоді ініціалізація змінної (екземпляру) структури буде наступна:

// ініціалізація value-структури
MyPoint_value mpv = { 3, 4, 5 };

Приклад 2. Нехай задано шаблон BookV, що описує книгу в бібліотеці.

value struct BookV
{
    char * title;
    char * author;
    int year;
    float price;
};

Ініціалізація структурної змінної буде наступна:

// ініціалізація value-структури
BookV bv1 = { NULL, NULL, 2002, 23.55 };
BookV bv2 = { "Title-1", "Author-1", 2000, 30.44 };
String ^s = gcnew String(bv2.title); // s = "Title-1"

11. Приклад оголошення та використання масиву ref-структур всередині ref-структури

Як відомо, оголосити масив native-структур та value-структур в ref-структурі не вдасться. У ref-структурі можна оголошувати тільки масив ref-структур.

Нехай задано шаблон ref-структури, що описує точку з кольором на площині.

// структура оголошена з кваліфікатором ref
ref struct MyPoint_ref
{
    int x;
    int y;
    int color;
};

Якщо потрібно оголосити масив точок MyPoint_ref у структурі, тоді треба написати такий код

// шаблон структури, в якій є масив точок (структур)
ref struct PointsArray_ref
{
    int n; // к-сть точок
    array <MyPoint_ref ^> ^mp;
};

У програмі (іншому програмному коді) можна використати шаблон PointsArray_ref приблизно наступним чином

// Покажчик на структуру PointsArray_ref
PointsArray_ref ^pA;

pA = gcnew PointsArray_ref; // виділення пам'яті

pA->n = 5;

// виділення пам'яті для покажчиків на MyPoint_ref
pA->mp = gcnew array <MyPoint_ref ^>(pA->n);

// виділення пам'яті для елементів масиву всередині структури
for (int i=0; i<pA->n; i++)
{
    pA->mp[i] = gcnew MyPoint_ref;
}

// заповнення структур значеннями
for (int i=0; i<pA->n; i++)
{
    pA->mp[i]->x = i*5+2;
    pA->mp[i]->y = i*i+3;
    pA->mp[i]->color = i;
}

// перевірка
int d;
d = pA->mp[3]->x; // d = 3*5+2 = 17
d = pA->mp[4]->y; // d = 4*4+3 = 19

12. Приклад оголошення та використання масиву value-структур всередині value-структури

Дано шаблон структури MyPoint_value, що описує точку з кольором на площині.

// структура оголошена з кваліфікатором value
value struct MyPoint_value
{
    int x;
    int y;
    int color;
};

Дано шаблон структури PointsArray_value, що містить масив точок (структур MyPoint_value).

// масив value-структур у структурі
value struct PointsArray_value
{
    int n; // кількість точок
    array <MyPoint_value> ^mp;
};

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

// масив value-структур в структурі
PointsArray_value mA;

mA.n = 5;

// виділення пам'яті для масиву
mA.mp = gcnew array <MyPoint_value>(mA.n);

// заповнення елементів структур значеннями
for (int i=0; i<mA.n; i++)
{
    mA.mp[i].x = i*5+2;
    mA.mp[i].y = i*i+5;
    mA.mp[i].color = i;
};

// перевірка
int d;
d = mA.mp[3].x;

13. Приклад оголошення та роботи з двовимірним масивом ref-структур

Нехай задано ref-структуру, що описує точку на площині

ref struct MyPoint_ref
{
    int x;
    int y;
    int color;
};

Демонстрація роботи з двовимірним масивом ref-структур розміром 5*10

// Двовимірний масив ref-структур. Етапи роботи
// 1. Оголошення змінної з іменем типу 'двовимірний масив ref-структур'
array <MyPoint_ref ^, 2> ^mp;

// 2. Виділення пам'яті для покажчиків на структури в масиві розміром 5*10
mp = gcnew array <MyPoint_ref ^,2>(5, 10);

// 3. Виділення пам'яті для елементів масиву - ref-структур MyPoint_ref
for (int i=0; i<5; i++)
    for (int j=0; j<10; j++)
        mp[i,j] = gcnew MyPoint_ref;

// 4. Заповнення довільними значеннями структур
for (int i=0; i<5; i++)
    for (int j=0; j<10; j++)
    {
        mp[i,j]->x = i + j*3;
        mp[i,j]->y = i*i + j;
        mp[i,j]->color = i + j;
    }

// 5. Перевірка
int d;
d = mp[2,4]->x; // d = 2+4*3 = 14
d = mp[0,3]->y; // d = 0*0+3 = 3
d = mp[3,8]->color; // d = 3+8 = 11

 

14. Приклад оголошення та роботи з двовимірним масивом value-структур

Нехай задано шаблон value-структури

value struct MyPoint_value
{
    int x;
    int y;
    int color;
};

Тоді робота з двовимірним масивом таких структур розміром 5*10 буде приблизно такою:

// Двовимірний масив value-структур. Етапи роботи
// 1. Оголошення змінної mp типу 'двовимірний value-масив MyPoint_value'
array <MyPoint_value, 2> ^mp;

// 2. Виділення пам'яті для двовимірного масиву
mp = gcnew array <MyPoint_value, 2>(5, 10);

// 3. Заповнення значеннями полів масиву
for (int i=0; i<5; i++)
    for (int j=0; j<10; j++)
    {
        mp[i,j].x = i*i + j;
        mp[i,j].y = i*5 - j;
        mp[i,j].color = i + j*2;
    }

// 4. Перевірка
int d;
d = mp[2,3].x; // d = 7
d = mp[3,0].color; // d = 3
d = mp[1,2].y; // d = 3


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