Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
+ООП_Навч_посібник.doc
Скачиваний:
8
Добавлен:
01.07.2025
Размер:
6.58 Mб
Скачать

8.2.2. Варіанти оголошень посилальних параметрів

У виданій в 1986 р. книзі "Язык программирования C++" (у якій був вперше описаний синтаксис мови C++) Б'єрн Страуструп представив стиль оголошення посилальних параметрів, який було схвалено іншими програмістами. Відповідно до цього стилю оператор "&" зв'язується з іменем типу, а не з іменем змінної. Наприклад, ось як виглядає ще один спосіб запису прототипу функції swap().

void swap(int& x, int& y);

Неважко помітити, що у цьому оголошенні символ "&" прилягає впритул до імені типу int, а не до імені змінної х.

Деякі програмісти визначають в такому стилі і покажчики, пов'язуючи символ "*" з типом, а не із змінною, як у наведеному прикладі:

float* р;

Наведені оголошення відображають бажання деяких програмістів мати у мові програмування C++ окремий тип посилання або покажчика. Але йдеться про те, що таке зв'язування символу "&" або "*" з типом (а не з іменем змінної) не поширюється на весь перелік змінних, які наводяться в оголошенні, що може призвести до плутанини. Наприклад, в наступному оголошенні створюється один покажчик (а не два) на цілочисельну змінну:

int* а, b;

У цьому записі b оголошується як цілочисельна змінна (а не як покажчик на цілочисельну змінну), оскільки, як визначено синтаксисом мови C++, використаний в оголошенні символ "*" або "&" пов'язується з конкретною змінною, якій він передує, а не з типом, за яким його знаходять.

Важливо розуміти, що для С++-компілятора абсолютно байдуже, як саме Ви напишете оголошення: int *p або int* р. Таким чином, якщо Ви вважаєте за краще пов'язувати символ "*" або "&" з типом, а не змінною, то вчиняйте так, як Вам зручно. Але, щоб уникнути надалі будь-яких непорозумінь, у цьому навчальному посібнику ми пов'язуватимемо символ "*" або "&" з іменем змінної, а не з іменем типу.

Варто знати! У мові С посилання не підтримуються. Тому єдиний спосіб забезпечити у мові С виклик за посиланням полягає у використанні покажчиків, як це було показано вище (див. першу версію функції swap()). Перетворюючи С-код в С++-код, Вам варто замість параметрів-покажчиків використовувати, де це можливо, посилання.

8.2.3. Повернення посилань

Функція може повертати посилання. У програмуванні мовою C++ передбачено декілька застосувань для посилальних значень, що повертаються функціями. Зараз ми продемонструємо тільки деякі з них, а інші розглянемо нижче у цьому навчальному посібнику, коли познайомимося з перевантаженням операторів (див. розд. 13).

Якщо функція повертає посилання, це означає, що вона повертає неявний покажчик на значення, що передається нею настановою return. Цей факт відкриває вражаючі можливості: функцію, виявляється, можна використовувати в лівій частині настанови присвоєння! Наприклад, розглянемо таку просту програму.

Код програми 8.5. Демонстрація повернення посилання

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

double &fun();

double num = 100.0;

int main()

{

double newval;

cout << fun() << "\n"; // Відображаємо значення num.

newval = fun(); // Присвоюємо значення num змінною newval.

cout << newval << "\n"; // Відображаємо значення newval.

fun() = 99.1; // Змінюємо значення num.

cout << fun() << "\n"; // Відображаємо нове значення num.

getch(); return 0;

}

double &fun()

{

return num; // Повертаємо посилання на num.

}

Ось як виглядають результати виконання цієї програми:

100

100

99.1

Розглянемо цю програму грунтовніше. Судячи з прототипу функції fun(), вона повинна повертати посилання на double-значення. За оголошенням функції fun() знаходиться оголошення глобальної змінної num, яка ініціалізувалася значенням 100. У процесі виконання такої настанови виводиться початкове значення змінної num:

cout << fun() << "\n"; // Відображаємо значення num.

Після виклику функція fun() повертає посилання на змінну num. Оскільки функція fun() оголошена з "зобов'язанням" повернути посилання, то у процесі виконання рядка

return num; // Повертаємо посилання на num.

автоматично повертається посилання на глобальну змінну num. Це посилання потім використовується настановою cout для відображення значення num.

У процесі виконання рядка

newval = fun(); // Присвоюємо значення num змінній newval.

посилання на змінну num, що повертається функцією fun(), використовують для присвоєння значення num змінній newval.

А ось найцікавіший рядок у програмі

fun() = 99.1; // Змінюємо значення num.

У процесі виконання цієї настанови присвоєння значення змінній num дорівнює числу 99,1. І ось чому: оскільки функція fun() повертає посилання на змінну num, то це посилання і є приймачем настанови присвоєння. Таким чином, значення 99,1 присвоюється змінній num опосередковано, через посилання на неї, яке повертає функція fun().

Нарешті, у процесі виконання рядка

cout << fun() << "\n"; // Відображаємо нове значення num.

відображається нове значення змінної num (після того, як посилання на змінну num буде повернено внаслідок виклику функції fun() у настанові cout).

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

Код програми 8.6. Демонстрація повернення функцією значення посилального типу

#include <iostream> // Для потокового введення-виведення

using namespace std; // Використання стандартного простору імен

double &change_fun(int izm); // Функція повертає посилання.

double vals[] = {1.1, 2.2, 3.3, 4.4, 5.5};

int main()

{

int i;

cout << "Ось початкові значення: ";

for(i=0; i<5; i++) cout << vals[i] << " ";

cout << "\n";

change_fun(1) = 5298.23; // Змінюємо значення 2-го елемента масиву.

change_fun(3) = -98.8; // Змінюємо значення 4-го елемента масиву.

cout << "Ось змінені значення: ";

for(i=0; i<5; i++) cout << vals[i] << " ";

cout << "\n";

getch(); return 0;

}

double &change_fun(int i)

{

return vals[i]; // Повертаємо посилання на i-й елемент.

}

Ця програма змінює значення другого і четвертого елементів масиву vals. Результати її виконання є такими:

Ось початкові значення: 1.1 2.2 3.3 4.4 5.5

Ось змінені значення: 1.1 5298.23 3.3 -98.8 5.5

Давайте з'ясуємо, як вони були отримані. Функція change_fun() оголошена як та, що повертає посилання на значення типу double. Кажучи конкретніше, вона повертає посилання на елемент масиву vals, який задано їй як параметр i. Таким чином, у процесі виконання такої настанови функції main()

change_fun(1) = 5298.23; // Змінюємо значення 2-го елемента масиву.

функція change_fun() повертає посилання на елемент vals[1]. Через це посилання елементові масиву vals[1] тепер присвоюється значення 5298,23. Аналогічні дії відбуваються у процесі виконання і цієї настанови:

change_fun(3) = -98.8; // Змінюємо значення 4-го елемента масиву.

Оскільки функція change_fun() повертає посилання на конкретний елемент масиву vals, то її можна використовувати в лівій частині настанови для присвоєння нового значення відповідному елементу масиву.

Проте, організовуючи повернення функцією посилання, необхідно поклопотатися про те, щоб об'єкт, на який вона посилається, не виходив за межі діючої області видимості. Наприклад, розглянемо таку функцію:

// Тут помилка: не можна повертати посилання на локальну змінну.

int &fun()

{

int izm = 10;

return izm;

}

При завершенні роботи функції fun() локальна змінна izm вийде за межі області видимості. Отже, посилання на змінну izm, що повертається функцією fun(), буде невизначеною. Насправді деякі компілятори не скомпілюють функцію fun() у такому вигляді, і саме з цієї причини. Проте проблема такого роду може бути створена опосередковано, тому потрібно уважно поставитися до того, на який об'єкт повертатиме посилання Ваша функція.