Обчислення значення функцій
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
Якщо зміна значення лівостороннього виразу небажана перетворюємо його на сталий