Покажчики. Частина 6. Складені native та managed типи даних. Керовані покажчики (^) в середовищі CLR. Виділення пам’яті. Кваліфікатори ref та value. Керовані покажчики (^) на структури, класи

Покажчики. Частина 6. Складені native та managed типи даних. Керовані покажчики (^) в середовищі CLR. Виділення пам’яті. Кваліфікатори ref та value. Керовані покажчики (^) на структури, класи


Зміст



1. Які особливості організації роботи складених типів даних в середовищі CLR (Common Language Runtime)?

У мові C++ (Visual C++) існує два види складених типів даних:

  • native-типи даних. Покажчики на такі типи даних позначаються символом *.
  • типи даних для роботи в середовищі CLR (Common Language Runtime). Такі типи ще називаються managed-типами даних або керованими типами даних. Такі типи даних розміщуються в пам’яті, яка виділяється середовищем CLR. Об’єкти цих типів даних повинні розміщуватись в динамічній пам’яті, яка керується середовищем CLR. Покажчики на таку пам’ять позначаються символом ^.

2. Які особливості використання складених native типів даних?

До складених native-типів даних відносяться:

  • native-структури;
  • native-класи.

Для доступу до таких типів даних використовується некерований покажчик * (unmanaged pointer).

Більш детально робота некерованих покажчиків описується в темах:

3. Які особливості використання керованих (managed) складених типів даних?

До керованих складених типів даних (managed) відносяться:

  • managed-структури;
  • managed-класи.

Такі структури і класи призначені для роботи в середовищі CLR. Вони розміщуються в пам’яті, яка виділяється і керується середовищем CLR. Якщо програміст створює проект типу “CLR Console Application” або “Windows Forms Application“, то цей проект задовольняє вимогам середовища CLR.

Об’єкти, що керуються CLR середовищем, попадають в динамічну пам’ять. При описі покажчика на managed-структуру або managed-клас використовується символ ^. Пам’ять для такого покажчика виділяється утилітою gcnew.

4. Які кваліфікатори використовуються для оголошення структур і класів в середовищі CLR?

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

  • кваліфікатор ref – означає, що структура або клас є посилального типу;
  • кваліфікатор value – означає, що структура або клас є типом значення.

В середовищі CLR можна також оголошувати структуру або клас без кваліфікатора. Робота з такими структурами використовується для сумісності з покажчиками на структури старіших версій C/C++.

5. Яка відмінність між кваліфікаторами ref і value при оголошенні структур або класів? Приклади доступу до ref-структур та value-структур за покажчиком

Відмінність між кваліфікаторами ref і value полягає у способі виділення пам’яті для покажчика на структуру або клас.

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

З модифікатором value структура або клас може бути розміщена і в керованій (managed) кучі і в native-кучі.

Нижченаведені приклади демонструють відмінність між структурами, що описані з кваліфікаторами ref та value. Те саме відноситься і до класів.

Нехай потрібно використати структуру, яка описує піксель на екрані монітора.

Приклад 1. Оголошення структури з кваліфікатором ref.

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

Тоді, використання структури з іншого програмного коду може бути таким:

// Кваліфікатор ref
// 1. Оголошення покажчика на структуру з кваліфікатором ref
MyPoint_ref ^ pr;
// 2. Виділення пам'яті для покажчика
//    Пам'ять виділяється в керованій (managed) "кучі"
pr = gcnew MyPoint_ref;

// 3. Заповнення полів структури
pr->x = 28;
pr->y = 35;
pr->color = 2;

// Помилка!
// MyPoint_ref * p; - неможна описати некерований покажчик на ref-структуру

Приклад 2. Оголошення структури з кваліфікатором value.

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

Використання структури

// Кваліфікатор value
// 1. Оголошення покажчиків на структуру з кваліфікатором value
MyPoint_value ^ p1; // managed-покажчик
MyPoint_value * p2; // native-покажчик - також працює

// 2. Виділення пам'яті для покажчиків
p1 = gcnew MyPoint_value;
p2 = new MyPoint_value;

// 3. Демонстрація роботи з полями структур через покажчики
p1->x = 33;
p1->y = 200;
p1->color = 3;

p2->x = 10;
p2->y = p1->y;
p2->color = p1->color;

6. Який доступ за замовчуванням мають поля та члени даних структури і класу?

При оголошенні структури, її поля (члени даних) та методи мають специфікатор доступу public.

При оголошенні класу, його члени даних та методи мають специфікатор доступу private.

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

Нехай задана структура, яка оголошена в модулі “MyRefStruct.h”. Структура описує координати точки на площині. Структура оголошена з кваліфікатором ref.

ref struct MyPointRef
{
    int x;
    int y;
};

Приклад використання структури в програмі з допомогою керованого (managed) покажчика.

// оголошення структури з кваліфікатором ref
MyPointRef ^mp; // покажчик на структуру
mp = gcnew MyPointRef; // виділення пам’яті для структури

// доступ до полів структури
mp->x = 25;
mp->y = 30;

8. Яким чином описуються managed-масиви в середовищі CLR? Призначення ключового слова array

В C++ native-масиви оголошуються звичним для мови способом. Більш детально про масиви в C++ описується в наступних статтях:

Щоб описати managed-масив використовується ключове слово array.

9. Приклад оголошення та використання масиву managed-структур, які оголошені з кваліфікатором ref, з допомогою керованого покажчика (^)

Нехай в модулі “MyRefStruct.h” оголошено структуру MyPointRef, яка описує точку на координатній площині

ref struct MyPointRef
{
    int x;
    int y;
};

Тоді, робота з масивом з 10 managed-структур типу MyPointRef продемонстрована нижче. Спочатку потрібно підключити файл структури:

// підключити файл структури
#include "MyRefStruct.h"

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

// масив m покажчиків на структуру,
// описану як managed з ключовим словом ref
array <MyPointRef^> ^m;

// виділення пам'яті для 10 покажчиків
m = gcnew array <MyPointRef^>(10);

// створення 10 об'єктів типу "структура MyPointRef"
for (int i=0; i<10; i++)
    m[i] = gcnew MyPointRef; // виділення пам'яті для i-ї структури

// заповнення полів x, y масиву структур
for (int i=0; i<10; i++)
{
    m[i]->x = i*5;
    m[i]->y = i*i;
}

Якщо оголосити структуру MyPointRef з кваліфікатором value, то даний приклад також буде працювати.

10. Приклад оголошення та використання managed-масиву на базові типи (int, double) з допомогою керованого (^) покажчика

У наведеному нижче прикладі оголошується масив з 20 цілих чисел типу int. Для цього використовується керований покажчик (^).

Виділення пам’яті для масиву відбувається в 2 етапи:

  1. Виділення пам’яті для 20 покажчиків на тип int.
  2. Виділення пам’яті для кожного елементу типу int, на які мають вказувати покажчики.

Для оголошення масиву використовується ключове слово array.

// managed-масив на базові типи
array <int ^> ^pi; // pi - покажчик на масив типів int
pi = gcnew array <int ^>(20); // виділення пам'яті для масиву з 20 покажчиків

// виділення пам'яті для кожного елементу масиву
for (int i=0; i<20; i++)
    pi[i] = gcnew int;  // виділення пам'яті для i-го елементу масиву

// використання масиву - заповнення значеннями
for (int i=0; i<20; i++)
    pi[i] = i*5;

int d;
d = (int)pi[2]; // d = 10

11. Приклад оголошення та використання массиву покажчиків (^) на клас, що оголошені як managed з використанням класифікатора ref та ключового слова array

У прикладі оголошено клас з іменем MyClassRef, що описує координати точки на площині. Клас оголошено як managed (має кваліфікатор ref). Клас містить:

  • внутрішні приховані (private) члени даних x, y;
  • конструктор класу MyClassRef();
  • методи GetX() та GetY(), що повертають значення членів даних x, y;
  • метод SetXY(), що встановлює нові значення членів даних x, y.

Клас описаний у двох модулях (файлах):

  • модуль “MyClassRef.h” – містить оголошення членів даних класу та його методів;
  • модуль “MyClassRef.cpp” – містить реалізацію методів класу.

Текст модуля “MyClassRef.h”:

#pragma once

// клас оголошено як managed
ref class MyClassRef
{
    // члени даних класу
    int x; // координата x
    int y; // координата y
    public:

    // методи класу
    MyClassRef(void);
    int GetX(void);
    int GetY(void);
    void SetXY(int nx, int ny);
};

Текст модуля “MyClassRef.cpp”:

#include "StdAfx.h"
#include "MyClassRef.h"

// конструктор класу
MyClassRef::MyClassRef(void)
{
    x = y = 0;
}

// методи класу
int MyClassRef::GetX(void)
{
    return x;
}

int MyClassRef::GetY(void)
{
    return y;
}

void MyClassRef::SetXY(int nx, int ny)
{
    x = nx;
    y = ny;
    return;
}

Щоб з іншого програмного коду використовувати методи класу треба попередньо підключити модуль “MyClassRef.h”:

#include "MyClassRef.h"

Після цього можна використовувати методи класу. Нижче продемонстровано оголошення та використання масиву керованих (managed) покажчиків (^) на клас з іншого програмного коду:

// масив mp managed-покажчиків на клас MyClassRef
// одразу виділяється пам'ять для 10 покажчиків
array <MyClassRef ^> ^mp = gcnew array <MyClassRef ^>(10);

// виділення пам'яті для кожного класу, на який вказує покажчик
for (int i=0; i<10; i++)
    mp[i] = gcnew MyClassRef;

// використання методів класу з допомогою масиву покажчиків
mp[1]->SetXY(5, 6);
mp[5]->SetXY(10, -200);

int d;
d = mp[1]->GetX(); // d = 5
d = mp[0]->GetY(); // d = 0
d = mp[5]->GetY(); // d = -200


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