Managed (керовані) та unmanaged (некеровані) класи у Visual C++. Огляд відмінностей роботи. Приклади реалізації managed та unmanaged класів

Managed (керовані) та unmanaged (некеровані) класи у Visual C++. Огляд відмінностей роботи. Приклади реалізації managed та unmanaged класів


Зміст



1. Які види класів існують у Visual C++? Які особливості використання різних видів класів?

У системі Visual C++ використовуються два види класів:

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

Робота з покажчиками на клас детально описується в темах:

2. Приклад використання класу, що описує масив з n чисел

Нижче наведено приклади реалізації класів двох видів:

  • unmanaged-класу (native-класу);
  • managed-класу.

За даними прикладами можна розробляти власні класи.

2.1. Unmanaged (некерований) клас
2.1.1. Робота з методами класу з допомогою об’єкту класу

У прикладі реалізовано unmanaged-клас MyArrayUnmanaged, що представляє собою масив чисел типу double розмірністю n.
У класі описуються такі внутрішні змінні:

  • n – поточна кількість елементів масиву (довжина масиву);
  • A – масив чисел типу double.

У класі реалізовано такі конструктори:

  • конструктор за замовчуванням MyArrayUnmanaged(). Цей конструктор занулює значення n;
  • конструктор, що ініціалізує значення n та масив A. Конструктор має 1 параметр.

У класі реалізовано такі методи:

  • GetN() – читає поточне значення n;
  • SetN() – встановлює нове значення n, автоматично занулюється масив A. Пам’ять для масиву перевизначається;
  • GetAi() – повертає значення заданого елементу масиву;
  • SetAi() – встановлює нове значення заданого елементу масиву;
  • GetArrayPtr() – демонстраційний метод, що повертає unmanaged-покажчик (*) на масив.
class MyArrayUnmanaged
{
    // оголошення масиву в unmanaged класі
    int n;
    double *A;

    public:
    MyArrayUnmanaged(void); // конструктор за замовчуванням
    ~MyArrayUnmanaged(void); // деструктор

    MyArrayUnmanaged(int nn) // конструктор з 1 параметром
    {
        n = nn;
        // виділення пам'яті для масиву
        A = new double[n];

        // заповнення масиву значеннями
        for (int i=0; i<n; i++)
            A[i] = i;
    }

    // методи класу
    // взяти значення n
    int GetN(void)
    {
        return n;
    }

    // встановити нове значення n
    void SetN(int nn)
    {
        // звільнити пам'ять, виділену для старого масиву
        if (n>0) delete A;

        n = nn;
        // виділити пам'ять для нового масиву
        A = new double[n];

        // занулити значення новоствореного масиву
        for (int i=0; i<n; i++)
            A[i] = 0;
    }

    // повернути значення масиву, що знаходиться в позиції i
    double GetAi(int i)
    {
        return (double)A[i];
    }

    // встановити нове значення масиву, що знаходиться в позиції i
    void SetAi(int i, double x)
    {
        A[i] = x;
    }

    // повернути покажчик на масив
    double * GetArrayPtr(void)
    {
        return (double *)A;
    }
};

// реалізація конструкторів класу
// Конструктор класу за замовчуванням
MyArrayUnmanaged::MyArrayUnmanaged(void)
{
    n = 0;
}

// Деструктор класу
MyArrayUnmanaged::~MyArrayUnmanaged(void)
{
    if (n>0) delete A;
}

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

// Демонстрація роботи з класом MyArrayUnmanaged

MyArrayUnmanaged MA; // об'єкт класу, викликається конструктор без параметрів
int d;
double x;

d = MA.GetN(); // d = 0

MyArrayUnmanaged MA2(15); // викликається конструктор з 1 параметром
d = MA2.GetN(); // d = 15

MyArrayUnmanaged MA3 = 25; // викликається конструктор з 1 параметром
d = MA3.GetN(); // d = 25
x = MA3.GetAi(5); // x = 5.0

MA3.SetAi(5, -3.45); // змінити значення в комірці 5 масиву MA3
x = MA3.GetAi(5); // x = -3.45

// використання методу GetArrayPtr()
double * B; // unmanaged-покажчик на double
B = MA3.GetArrayPtr(); // B вказує на масив A об'єкту MA3

x = B[3]; // x = 3.0
x = B[5]; // x = -3.45

// використання методу SetN()
MA3.SetN(5);
d = MA3.GetN(); // d = 5
x = MA3.GetAi(3); // x = 0

2.1.2. Робота з методами класу з допомогою покажчика (*) на клас

Продемонстрована робота з unmanaged-покажчиком на клас.

MyArrayUnmanaged * pA; // оголосити покажчик на клас
pA = new MyArrayUnmanaged(10); // викликається конструктор з 1 параметром

int d;
double x;

d = pA->GetN(); // d = 10
x = pA->GetAi(3); // x = 3

2.2. Приклад реалізації managed (керованого) класу

У наведеному нижче прикладі реалізовано managed-клас CMyArray, що реалізує масив з n дійсних (double) чисел. Розмірність масиву змінюється динамічно в залежності від потреби.
У класі реалізовано такі внутрішні (private) змінні:

  • n – поточна кількість елементів масиву;
  • A – масив чисел.

Клас містить 2 конструктори:

  • CMyArray() без параметрів. Це є конструктор за замовчуванням. У цьому конструкторі значення n встановлюється рівним нулю;
  • CMyArray() з одним параметром, що рівний розміру масиву n. Тут значення елементів масиву встановлюється рівним номеру індексу масиву.

Методи класу наступні:

  • GetN() – отримати поточну довжину масиву n;
  • SetN() – встановити нове значення n і занулити значення елементів масиву A;
  • GetAi() – отримати поточне значення елементу масиву з індексом i;
  • GetArrayA() – отримати цілий масив елементів;
  • SetAi() – встановити нове значення в елемент масиву з індексом i.

Реалізація класу міститься в декількох модулях (файлах):

  • файл MyArray.h – містить код класу CMyArray;
  • файл MyArray.cpp – містить реалізацію методів класу. У даному випадку цей модуль містить реалізацію конструкторів класу. Реалізація інших методів описується в самому класі.
    Текст модуля MyArray.h наступний:
// managed клас CMyArray
ref class CMyArray
{
    // оголошення масиву розміром n в managed-класі
    int n;
    array <double^> ^A;

    public:
    // Конструктор за замовчуванням
    CMyArray(void);

    // конструктор з одним параметром
    CMyArray(int nn);

    // Методи класу
    // взяти значення n
    int GetN(void)
    {
        return n;
    }

    // встановити нове значення n
    void SetN(int nn)
    {
        if (n>0) delete A;

        n = nn; // нове значення
        A = gcnew array <double^>(n);
        for (int i=0; i<n; i++)
            A[i] = gcnew double;

        for (int i=0; i<n; i++)
            A[i] = (double)0;
    }

    // взяти елемент масиву з індексом i (i = 1..n-1)
    double GetAi(int i)
    {
        return (double)A[i];
    }

    // взяти покажчик на масив
    array <double^> ^ GetArrayA(void)
    {
        return A;
    }

    // встановити нове значення в елемент масиву з індексом i (i=1..n-1)
    void SetAi(int i, double nx)
    {
        A[i] = nx;
    }
};

Текст модуля MyArray.cpp наступний:

#include "StdAfx.h"
#include "MyArray.h"

// конструктор за замовчуванням
CMyArray::CMyArray(void)
{
    n = 0;
}

// конструктор з 1 параметром
CMyArray::CMyArray(int nn)
{
    n = nn;

    // виділення пам'яті для масиву (перевиділення)
    A = gcnew array <double ^>(n);

    // виділення пам'яті для кожної комірки масиву
    for (int i=0; i<n; i++)
        A[i] = gcnew double;

    // заповнення значеннями елементів масиву
    for (int i=0; i<n; i++)
        A[i] = (double)i;
}

2.2.1. Робота з методами класу з допомогою об’єкту класу

Щоб використовувати даний клас в інших модулях (файлах) потрібно підключити модуль MyArray.h

#include "MyArray.h"

Наведений нижче програмний код демонструє використання методів класу CMyArray.

// Демонстрація роботи з об'єктом managed-класу
// 1. Виклик конструктора без параметрів (викликається автоматично)
CMyArray MA; // об'єкт класу CMyArray, викликається конструктор без параметрів CMyArray()...

int d;
d = MA.GetN(); // d = 0

// 2. Виклик конструктора з 1 параметром при створенні об'єкту, спосіб 1
CMyArray MA2(15); // викликаєтсья конструктор CMyArray(15)

int d2 = MA2.GetN(); // d2 = 15
double x;
x = MA2.GetAi(5); // x = 5.0

// 3. Виклик конструктора з 1 параметром при створенні об'єкту, спосіб 2
CMyArray MA3 = 20; // викликається конструктор CMyArray(20)
int d3;
d3 = MA3.GetN(); // d3 = 20
x = MA3.GetAi(18); // x = 18.0

// 4. Демонстрація повернення масиву з методу класу GetArrayA
CMyArray MA4 = 30; // в масиві 30 елементів
array <double^> ^B;

B = MA4.GetArrayA(); // Взяти цілий масив A з класу
x = (double)B[22]; // x = 22.0

// 5. Метод SetAi
CMyArray MA5 = 10; // оголосити масив з 10 елементів
MA5.SetAi(3, 2.85); // виклик методу SetAi
x = MA5.GetAi(3); // x = 2.85

// 6. Метод SetN
MA5.SetN(30);
d = MA5.GetN(); // d = 30
x = MA5.GetAi(3); // x = 0

Пояснимо деякі фрагменти коду. При оголошенні об’єкту класу

CMyArray MA;

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

CMyArray MA2(10);

або

CMyArray MA2 = 10;

де число 10 означає розмірність масиву.

Методи обробки даних класу описуються в розділі public. Виклик методів здійснюється стандартним способом з допомогою символу ..

2.2.2. Робота з методами класу з допомогою покажчика (^) на клас

Також можна описувати покажчик на managed-клас. Наведений нижче код демонструє використання покажчика на клас CMyArray.

// managed-покажчик на клас
CMyArray ^ pA;
pA = gcnew CMyArray(10); // виділення пам'яті для класу (масиву з 10 елементів), виклик конструктора

double x;
x = pA->GetAi(5); // x = 5, доступ до методів класу

У наведеному вище коді в рядку

pA = gcnew CMyArray(10);

викликається конструктор CMyArray() з одним параметром (параметризований констуктор). В конструкторі здійснюється виділення пам’яті для масиву та заповнення елементів значеннями.


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