Обчислення значення функцій


4.4. Обчислення значення функції — вихід із функції; особливості повернення та використання іменованого значення, іменованої константи

 

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

 

int min (int x, int y)

{

return x < y ? x : y;

}

 

У випадку повернення іменованих (лівосторонніх) значень можливі проблеми. Нехай маємо визначення структури дерева

 

struct Tree

{

int node;

Tree *left;

Tree *right;

};

 

Як можна уявити собі створення його вузла. Наприклад, так: локальна змінна

 

Tree theTree;

 

резервує пам’ять для полів структури, а їх заповнення відбувається у функції

 

Tree createTree ( int node, Tree * left, Tree * right)

{

Tree aTree;

aTree.node = node;

aTree.left = left;

newTree.right = right;

return aTree;

}

 

ЇЇ виклик очевидний, наприклад, такий

 

theTree = createTree (1, 0, 0);

 

Інший спосіб полягає у визначенні указника

 

Tree *theTreePtr;

 

йому необхідно відвести пам’ять під вершину і викликати функцію

 

theTreePtr = new Tree;

theTreePtr = createTree (1, 0, 0);

 

 

Хотілося б уникнути копіювання і одразу розмістити указник на збудований вузол. Цього можна було б досягти, додавши до функції ще один параметр

 

void createTree (Tree *aTree,

int node, Tree *left, Tree *right)

 

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

 

Tree* createTree ( int node, Tree *left, Tree *right)

{

// Помилка: повернення адреси локального об’єкту

Tree aTree;

aTree.node = node;

aTree.left = left;

newTree.right = right;

return &aTree;

}

 

або ще не зрозуміліше

 

Tree& createTree ( int node, Tree *left, Tree *right)

{

// Помилка: повернення адреси локального об’єкту

Tree aTree;

aTree.node = node;

aTree.left = left;

newTree.right = right;

return aTree;

}

 

Інша справа, якщо в підпрограмі використати указники

 

Tree* createTreePtr (int node, Tree *left, Tree *right)

{

Tree *aTree;

aTree = new Tree;

aTree -> node = node;

aTree -> left = left;

aTree -> right = right;

return aTree;

}

 

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

 

Tree *theTree;

theTree = createTreePtr (1, 0, 0);

 

Ось трохи складніший приклад

 

int& getValue (int *vi, int ix, int sx)

{

if (ix>sx) return 0;

return vi [ ix ];

}

 

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

 

a = getValue( v, i, n);

 

Але ж функція, що повертає лівостороннє значення сама може стати зліва

 

getValue( v, i, n) = a;

 

і тоді значення a буде записане до масиву або ж такий виклик

 

getValue(v,1,5)++; //збільшує v[1] на 1

 

Якщо зміна значення лівостороннього виразу небажана перетворюємо його на сталий