Посилання (references). Посилання на змінні, структури, об’єднання та об’єкти класів. Відмінності між посиланнями та покажчиками. Приклади

Посилання (references). Посилання на змінні, структури, об’єднання та об’єкти класів. Відмінності між посиланнями та покажчиками. Приклади


Зміст


1. Як оголосити посилання на змінну? Загальна форма. Символ &

Під час оголошення, посилання має бути обов¢язково ініціалізоване. У найпростішому випадку посилання оголошується як показано нижче:

type & ref_var = var;

де

  • type – тип, на який оголошується посилання;
  • ref_var – змінна типу “посилання” на type;
  • var – змінна типу type, на яку оголошується посилання.

2. Приклади оголошення та використання посилання на змінну, структуру, об’єднання

Приклад 1. Оголошення посилання з іменем rd на змінну цілого типу d.

int d; // змінна
int & rd = d; // посилання з іменем rd на змінну d, ініціалізується при оголошенні

d = 0;
rd = 2; // d = 2

/*
int & rd2; // помилка (посилання має бути ініціалізоване при його оголошенні)
*/

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

Нехай задано тип структури, що описує дату:

struct Date
{
    int day;
    int month;
    int year;
};

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

Date d;
Date & rd = d; // посилання на структуру Date

// доступ до полів структури за посиланням
rd.day = 14; // d.day = 14
rd.month = 1; // d.month = 1
rd.year = 1972; // d.year = 1972

Приклад 3. Оголошення та використання посилання на об’єднання

Нехай задано об’єднання TYPES:

union TYPES
{
    int d[2];
    char c[4];
    float x;
};

Демонстрація використання посилання на об¢єднання TYPES:

TYPES t; // t - змінна типу "об'єднання"
TYPES & rt = t; // посилання на t
char c;
int d;

rt.c[0] = 'A';
c = t.c[0];

rt.d[1] = 28;
d = t.d[1];

3. Яка відмінність між посиланням та покажчиком?

Між посиланням та покажчиком можна виділити такі основні відмінності:

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


4. Як оголосити посилання на об’єкт деякого класу? Приклад оголошення та використання

Нехай задано клас CFloat. У класі оголошується внутрішня змінна типу float, конструктори та методи доступу Get(), Set().

Приклад демонструє використання посилання на об’єкт класу CFloat.

Програмний код модуля, створеного за шаблоном Win32 Console Application який демонструє використання посилання на об’єкт класу, має вигляд:

#include "stdafx.h"
#include <iostream>
using namespace std;

class CFloat
{
    float x;

    public:
    // конструктори класу
    CFloat() { x = 0; }
    CFloat(float nx) { x = nx; }

    // методи доступу
    float Get() { return x; }
    void Set(float nx) { x = nx; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    // посилання на об'єкт класу CFloat
    CFloat f; // f - об'єкт класу CFloat
    CFloat & rf = f; // rf - посилання на об'єкт класу CFloat
    float x;

    rf.Set(5.5); // доступ до методів класу через посилання
    x = f.Get(); // x = 5.5

    f.Set(-8.2);
    x = rf.Get(); // x = -8.2

    return 0;
}

5. Чи можна оголосити посилання на тип void? Чи існує тип void& ?

Ні.

6. Чи можна змінити значення за посиланням, якщо воно оголошене з ключовим словом const (оголошене як константне)?

Ні.

7. Як з допомогою посилання передати змінну чи об’єкт класу у функцію? Приклад

З допомогою посилання можна передавати змінну чи об’єкт класу у функцію. Таким чином, у тілі функції можна змінювати значення змінної, що була передана.

Приклад 1. Демонстрація передачі змінної типу int у функцію та зміни її значення в тілі функції. Додаток типу Win32 Console Application.

#include "stdafx.h"
#include <iostream>
using namespace std;

// функція Inc5() - збільшує на 5
void Inc5(int & t) // передача t за посиланням
{
    t += 5;
}

int _tmain(int argc, _TCHAR* argv[])
{
    int d;

    d = 5;
    Inc5(d); // d = 10
    Inc5(d); // d = 15

    return 0;
}

Приклад 2. У прикладі демонструється передача об’єкту класу за посиланням. Нехай задано клас CRadius та функцію Volume(). У функцію Volume() передається об’єкт класу CRadius за посиланням.

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

Текст програми, створеної за шаблоном Win32 Console Application:

#include "stdafx.h"
#include <iostream>
using namespace std;

class CRadius
{
    double radius;

    public:
    // конструктори
    CRadius() { radius = 1; }
    CRadius(double nradius) { radius = nradius; }

    // методи доступу
    void Set(double nradius) { radius = nradius; }
    double Get() { return radius; }
};

// обчислює об'єм кулі
double Volume(CRadius & R) // передача за посиланням
{
    double vol, radius;
    radius = R.Get();
    vol = 4.0 / 3.0 * 3.1415 * radius * radius * radius;
    return vol;
}

int _tmain(int argc, _TCHAR* argv[])
{
    CRadius r(2.5); // екземпляр класу CRadius
    double vol;
    vol = Volume(r); // передача об'єкту класу за посиланням
    cout << vol << endl; // vol = 65.4479
    return 0;
}

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

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

Приклад 1. Демонструється повернення з функції посилання на тип int.

#include "stdafx.h"
#include <iostream>
using namespace std;

// повернення посилання з функції
int & Inc5(int t) // функція збільшує на 5 значення параметру t
{
    t = t + 5;
    return t;
}

int _tmain(int argc, _TCHAR* argv[])
{
    int a, b;

    b = 7;
    a = Inc5(b); // a = 12
    cout << a << endl;

    return 0;
}

Приклад 2. Демонструється зміна значення зовнішнього (глобального) масиву A з допомогою функції ChangeA(), яка повертає посилання на елемент масиву A з заданим індексом.

Текст демонстраційної програми, створеної за шаблоном Win32 Console Application має наступний вигляд

#include "stdafx.h"
#include <iostream>
using namespace std;

#define MAX 20
double A[MAX];

// функція, що повертає посилання на елемент масиву A з індексом index
double & ChangeA(int index)
{
    // повернути значення елементу масиву A
    return A[index];
}

int _tmain(int argc, _TCHAR* argv[])
{
    int i;

    // заповнення масиву
    for (i=0; i<MAX; i++)
        A[i] = i * 2; // A = { 0.0, 2.0, 4.0, ..., 38.0 }

    // змінити значення елементу масиву з індексом 5 з допомогою функції
    ChangeA(5) = 3.25; // A[5] = 3.25;
    ChangeA(MAX-1) = -7.77; // A[19] = -7.77;

    return 0;
}

9. Приклади ініціаліалізації структурної змінної з використанням посилань

Показано два способи ініціалізації структурної змінної.

Приклад 1. Повернення посилання на структурну змінну. Ініціалізація структурної змінної здійснюється в функції InitPixel().

#include "stdafx.h"
#include <iostream>
using namespace std;

// структура піксель
struct Pixel
{
    int x, y, color;
};

// функція, що повертає посилання на структуру
Pixel & InitPixel(int value)
{
    Pixel px;
    px.x = px.y = px.color = value;
    return px;
}

int _tmain(int argc, _TCHAR* argv[])
{
    Pixel px;
    Pixel px2 = InitPixel(5); // px2.x = 5; px2.y = 5; px2.color = 5;
    px = InitPixel(11); // px.x = 11; px.x = 11; px.x = 11;
    cout << px.x << " - " << px.y << " - " << px.color << endl;
    cout << px2.x << " - " << px2.y << " - " << px2.color << endl;
    return 0;
}

Приклад 2. Ініціалізація структурної змінної типу Pixel. Функція InitPixel() отримує посилання на структурну змінну в якості параметра.

#include "stdafx.h"
#include <iostream>
using namespace std;

// структура піксель
struct Pixel
{
    int x, y, color;
};

// функція, що повертає посилання на структуру
void InitPixel(Pixel & px, int x, int y, int color)
{
    // зміна значення структурної змінної в функції
    px.x = x;
    px.y = y;
    px.color = color;
    return;
}

int _tmain(int argc, _TCHAR* argv[])
{
    Pixel px;
    Pixel px2;
    InitPixel(px, 3, 8, 10); // px.x = 3; px.y = 8; px.color = 11;
    InitPixel(px2, 5, 5, 8); // px2.x = 5; px2.y = 5; px2.color = 8;
    cout << px.x << " - " << px.y << " - " << px.color << endl;
    cout << px2.x << " - " << px2.y << " - " << px2.color << endl;
    return 0;
}

10. Які існують обмеження на роботу з посиланнями в C++?

У мові C++ робота з посиланнями (references) має свої обмеження:

  • посилання не можуть зв’язуватись з іншими посиланнями;
  • не існує нульового посилання;
  • посилання мають бути ініціалізовані при їх оголошенні.


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