Указатели. Часть 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


Связанные темы