Структури. Частина 4. Структури і функції. Передача структури у функцію в середовищі CLR. Повернення структури з функції

Структури. Частина 4. Структури і функції. Передача структури у функцію в середовищі CLR. Повернення структури з функції


Зміст



1. Які існують способи передачі структури у функцію?

Існує 2 способи передачі native-структури у функцію:

  • передача структури за значенням. При такій передачі робиться копія структурної змінної в пам’яті. Якщо структура має великий розмір, то такий спосіб неефективний. Перевагою цього способу є те, що всі маніпуляції з копією структури у функції не впливають на вихідну змінну;
  • передача покажчика на структуру. У цьому випадку передається тільки покажчик на структуру. Якщо структура займає великий об’єм пам’яті, то такий спосіб забезпечує швидку передачу значень структурної змінної у функцію. Це пов’язано з тим, що не робиться в пам’яті копії структурної змінної. Недоліком цього способу є те, що у функції випадково можна змінити значення вихідної структурної змінної, коли цього не потрібно робити.

2. Які існують способи повернення функцією структури?

Так само, як і при передачі структури у функцію (див. п.1), існують 2 способи повернення:

  • повернення структури за значенням;
  • повернення покажчика на структуру.

Переваги і недоліки кожного способу такі самі, як описується в п. 1.

3. Приклад передачі native-структури у функцію за значенням

Нехай у модулі “MyStruct.h” дано оголошення native-структури:

// native-структура, що описує точку на координатній площині
struct MyPoint
{
    int x;
    int y;
};

Нехай в деякому класі оголошується функція EqualPoints(), яка порівнює дві точки на координатній площині. Функція повертає true, якщо точки еквівалентні (рівні). В іншому випадку функція повертає false.

// функція, що порівнює на рівність дві точки
public: bool EqualPoints(MyPoint p1, MyPoint p2)
{
    bool f = false;
    if ((p1.x == p2.x) && (p1.y == p2.y))
        f = true;
    return f;
}

Тоді використання функції EqualPoints() може бути таким:

// підключення модуля "MyStruct.h"
#include "MyStruct.h"

...

// передача native-структури у функцію за значенням
bool f_equal;
MyPoint pt1, pt2; // p1, p2 – змінні типу "структура"

// заповнення значеннями
pt1.x = 23;
pt1.y = 35;
pt2.x = 23;
pt2.y = 35;

f_equal = this->EqualPoints(pt1, pt2); // f_equal = true

pt1.x = 100;
f_equal = EqualPoints(pt1, pt2);

4. Приклад передачі native-структури у функцію за покажчиком

Нехай задано native-структуру:

// native-структура
struct MyPoint
{
    int x;
    int y;
};

Нижче наведена функція, яка порівнює на рівність значення структурних змінних. Функція отримує покажчик на структуру.

// передача покажчика на структуру MyPoint
bool EqualPointsP(MyPoint * p1, MyPoint * p2)
{
    bool f = false;
    if ((p1->x == p2->x) && (p1->y == p2->y))
        f = true;
    return f;
}

Програмний код, який демонструє використання функції EqualPointsP().

// Передача структури за покажчиком
MyPoint p1;
MyPoint p2;

bool f_equal;
p1.x = 28;
p1.y = 35;
p2.x = 28;
p2.y = 35;

f_equal = EqualPointsP(&p1, &p2); // f_equal = true
p2.y = 100;
f_equal = EqualPointsP(&p1, &p2); // f_equal = false

5. Як передати у функцію managed-структуру, яка оголошена з кваліфікатором ref? Приклад

Нехай задано наступну ref-структуру.

// ref-структура
ref struct MyPointRef
{
    int x;
    int y;
};

Структури з кваліфікатором ref є структурами посилального типу. Для таких структур пам’ять повинна виділятись з допомогою утиліти gcnew. Тому, у функцію можна передати тільки посилання на такі структури.

Нехай дано функцію LengthRef(), яка визначає довжину лінії, яка з’єднує 2 точки. Функція отримує дві ref-структури в якості параметрів.

// Функція, що визначає довжину між двома точками
float LengthRef(MyPointRef ^p1, MyPointRef ^p2)
{
    float l;
    l = Math::Sqrt((p1->x-p2->x)*(p1->x-p2->x) + (p1->y-p2->y)*(p1->y-p2->y));
    return l;
}

Використання функції LengthRef() в деякому програмному коді.

// передача ref-структури у функцію
MyPointRef ^ pt1;
MyPointRef ^ pt2;
float len;

// виділення пам'яті для ref-структур
pt1 = gcnew MyPointRef;
pt2 = gcnew MyPointRef;

// заповнення ref-структур значеннями
pt1->x = 35;
pt1->y = 35;
pt2->x = 40;
pt2->y = 40;

len = LengthRef(pt1, pt2); // len = 7.071068

6. Як передати у функцію managed-структуру, яка оголошена з кваліфікатором value? Приклад

Managed-структуру, оголошену з кваліфікатором value можна передати у функцію одним з двох способів:

  • за значенням;
  • за покажчиком.

Нижче наведено обидва способи для value-структури MyPointValue, яка має таке оголошення.

// value-структура
value struct MyPointValue
{
    int x;
    int y;
};

Спосіб 1.

Нехай задана функція LengthValue(), яка визначає відстань між двома точками. Функція отримує вхідним параметром дві структури типу MyPointValue. Структури передаються як параметри-значення.

// визначення відстані між двома точками
float LengthValue(MyPointValue p1, MyPointValue p2)
{
    float len;
    len = (float)Math::Sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
    return len;
}

Приклад використання функції LengthValue() у програмі.

// передача value-структури за значенням
MyPointValue pt1;
MyPointValue pt2;
float len;

pt1.x = 35;
pt1.y = 50;
pt2.x = 40;
pt2.y = 55;
len = LengthValue(pt1, pt2); // len = 7.071068

Спосіб 2. Передача структури за покажчиком.

Нехай задана функція LengthValueP(), яка отримує два покажчики на структури типу MyPointValue.

// функція отримує покажчик на value-структуру
float LengthValueP(MyPointValue *p1, MyPointValue *p2)
{
    float len;
    len = Math::Sqrt((p1->x-p2->x)*(p1->x-p2->x) + (p1->y-p2->y)*(p1->y-p2->y));
    return len;
}

Використання функції LengthValueP() у програмі.

// передача value-структури за покажчиком
MyPointValue pt1;
MyPointValue pt2;
float len;

pt1.x = 0;
pt1.y = 0;
pt2.x = 10;
pt2.y = 10;

len = LengthValueP(&pt1, &pt2); // len = 14.14214

7. Як повернути екземпляр native-структури з функції? Приклад

Повернути native-структуру можна:

  • за значенням;
  • за адресою.

Нехай задано структуру MyPoint

// native-структура
struct MyPoint
{
    int x;
    int y;
};

Нехай у програмі реалізовано функцію GetCenterLine(), яка обчислює координати середини відрізку, що з’єднує 2 точки. Функція отримує 2 параметри – координати кінців відрізка.

// функція, що повертає native-структуру
// функція обчислює координати середини відрізку
MyPoint GetCenterLine(MyPoint p1, MyPoint p2)
{
    MyPoint res;
    res.x = (p1.x + p2.x)/2;
    res.y = (p1.y + p2.y)/2;
    return res;
}

Демонстрація повернення native-структури за значенням.

MyPoint pt1;
MyPoint pt2;
MyPoint resP; // результуюча точка, пам'ять має бути виділена

pt1.x = 30;
pt1.y = 20;
pt2.x = 36;
pt2.y = 40;

resP = GetCenterLine(pt1, pt2); // resP - координати середини відрізка

8. Приклад повернення з функції покажчика на native-структуру

Нехай задано структуру MyPoint

// native-структура
struct MyPoint
{
    int x;
    int y;
};

Нехай реалізовано функцію GetCenterLineP(), яка отримує 2 точки і повертає покажчик на структуру. У тілі функції реалізовано виділення пам’яті для структури оператором new.

MyPoint * GetCenterLineP(MyPoint p1, MyPoint p2)
{
    MyPoint * resP; // покажчик на структуру MyPoint

    // виділення пам'яті для структури динамічно
    resP = new MyPoint;

    // обчислення середини відрізку - заповнення значеннями
    resP->x = (p1.x + p2.x)/2;
    resP->y = (p1.y + p2.y)/2;

    return resP;
}

Демонстрація роботи з функцією GetCenterLineP().

// повернення native-структури за покажчиком
MyPoint * resPt; // покажчик на структуру – пам’ять для структури ще не виділена
MyPoint pt1, pt2; // екземпляри структури

pt1.x = 28;
pt1.y = 40;
pt2.x = 38;
pt2.y = 50;

// виклик функції GetCenterLineP()
// для покажчика resPt пам'ять виділяється всередині функції
resPt = GetCenterLineP(pt1, pt2);

// перевірка
int d;
d = resPt->x; // d = 33
d = resPt->y; // d = 45

9. Як повернути екземпляр value-структури з функції?

Якщо в середовищі CLR описана структура з кваліфікатором value, то повернення екземпляру такої структури з функції нічим не відрізняється від повернення native-структури (див. п. 7.).

10. Приклад повернення покажчика (^) на value-структуру

Якщо структура оголошена з кваліфікатором value, то функція може повертати покажчик на цю структуру. Допускається використання двох видів покажчиків:

  • класичного покажчика, який позначається символом ‘*’. Повернення такого покажчика з функції не відрізняється від повернення покажчика на native-структуру (див. п. 8);
  • покажчика, призначеного для роботи з об’єктами середовища CLR. Такий покажчик позначається символом ‘^’. Пам’ять для такого покажчика виділяється утилітою gcnew. Нижче наведено приклад повернення з функції такого покажчика на value-структуру.

Нехай оголошено структуру з кваліфікатором value

// value-структура
value struct MyPointValue
{
    int x;
    int y;
};

Нехай потрібно реалізувати функцію GetCenterLineVP(), яка отримує 2 точки і повертає покажчик на результуючу value-структуру. Результуюча структура містить координати центру відрізку, що з’єднує точки. Функція має такий вигляд:

// функція повертає покажчик на value-структуру 
MyPointValue ^ GetCenterLineVP(MyPointValue p1, MyPointValue p2) 
{
    MyPointValue ^ pv; // покажчик на структуру MyPointValue
    pv = gcnew MyPointValue;
    pv->x = (p1.x + p2.x)/2; 
    pv->y = (p1.y + p2.y)/2;
    return pv;
}

Демонстрація виклику функції з іншого програмного коду:

// створення екземплярів структур з одночасною ініціалізацією
MyPointValue pt1 = { 20, 30 };
MyPointValue pt2 = { 40, 60 };

// покажчик на структуру MyPointValue
MyPointValue ^ resPt;

// виклик функції GetCenterLineVP()
// всередині функції виділяється пам'ять для структури,
// на яку вказує resPt
resPt = GetCenterLineVP(pt1, pt2);

// перевірка
int d;
d = resPt->x; // d = 30
d = resPt->y; // d = 45

11. Приклад повернення з функції структури, яка оголошена з кваліфікатором ref

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

Нехай оголошено структуру

// ref-структура
ref struct MyPointRef
{
    int x;
    int y;
};

Тоді функція, що отримує 2 точки в якості параметрів і знаходить центр між двома точками буде мати приблизно наступний вигляд:

// функція, що повертає ref-структуру
MyPointRef ^ GetCenterLineRef(MyPointRef ^p1, MyPointRef ^p2)
{
    MyPointRef ^resPt;

    // виділення пам’яті для покажчика resPt
    resPt = gcnew MyPointRef;

    // заповнення значеннями полів покажчика
    resPt->x = (p1->x + p2->x)/2;
    resPt->y = (p1->y + p2->y)/2;

    return resPt;
}

Демонстрація використання функції в іншому програмному коді.

// покажчики на ref-структуру
MyPointRef ^pt1;
MyPointRef ^pt2;
MyPointRef ^resPt;

// виділення пам'яті для покажчиків pt1, pt2
pt1 = gcnew MyPointRef;
pt2 = gcnew MyPointRef;

// заповнення значеннями
pt1->x = 20;
pt1->y = 20;
pt2->x = 30;
pt2->y = 40;

// виклик функції GetCenterLineRef()
// пам'ять для покажчика resPt виділяється всередині функції
resPt = GetCenterLineRef(pt1, pt2);

// перевірка
int d;
d = resPt->x; // d = 25
d = resPt->y; // d = 30


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