Част 14. Връщане на стойност от функциите
Досега функциите, които разглеждахме, с изключение на обяснението на главната функция в част 5, не връщаха стойност и бяха дефинирани с ключовата дума void пред името си. Изчисляваната стойност се присвояваше на (променлива на) обекта, чиято функция беше извиквана. Но ако стойността, която искаме да бъде върната от функцията, не е от типа на обекта, такъв подход е невъзможен и функцията трябва да се дефинира да връща стойност.
1. Намиране модула на комплексно число. В част 10 въведохме клас, който представя комплексните числа. Също така, в уводния материал на лекцията се запознахме с комплексните числа и някои операции с тях, както и какво е това модул на комплексно число, който още се нарича големина или абсолютна стойност на комплексното число. Ето една кратка програма, която пресмята модула на комплексни числа:
//
complex1.cpp
//
a class that models a complex number data type
#include <iostream.h>
class
Complex_number
{
private:
float real; // real
part
float imaginary; // imaginary part
public:
void set()
{
char dummy; // for comma
cout << "Enter number (format Re, Im): ";
cin >> real >> dummy >> imaginary;
}
void display()
{
cout << '('
<< real // real part
<< ','
<< imaginary // imaginary part
<< ')';
}
float module()
{
float m = real*real + imaginary*imaginary;
return sqrt(m);
}
};
// class Complex_number
void
main()
{
Complex_number c1; // create a complex_number variable
char choice;
do
{
cout << "For c1, ";
c1.set(); // set c1
// calculate module of c1 and assign it to mod
float mod = c1.module();
cout << "Module of ";
c1.display(); // display
t3
cout << " = " << mod;
cout << "\nDo another (y/n)? ";
cin >> choice;
} while(choice != 'n');
} //end main
Функцията module() пресмята модула на обекта (комплексното число), за който се извиква. Тя се дефинира да връща стойност от реален тип float като за целта пред името се изписва типът на връщаната стойност: float module(). В първия ред на тялото на функцията се изчислява сума от квадратите на съставните части на комплексното число. Обърнете внимание на ключовата дума (keyword) return в следващия ред - на този ред директно се изчислява корен квадратен от спомената сума с функцията sqrt() и този корен директно се връща от функцията. В програмата (в main()) съответното присвояване на резултата от функцията става с твърдението float mod = c1.module();, което показва че се изчислява модулът на комплексното число c1 и този модул се присвоява на променливата от реален тип mod.
2. Твърдението (statement) return. Всяка една функция като бъде извикана изпълнява кода в своето тяло и след свършване на изпълнението се връща на реда, намиращ се след реда, който я извиква (казва се - изпълнява се реда - понеже обичайна практика е да се пише едно програмно твърдение (statement) на един програмен ред). При функциите, които връщат стойност е необходимо присъствието на твърдението return в тялото на функцията. То започва с ключовата дума return и след нея следва връщаната стойност, например return m;. Вместо конкретна стойност след return може да стои израз, точно както е във функцията module().
return sqrt(m);
След твърдението return програмата веднага излиза от функцията и изпълнява следващото твърдение след повикването на функцията. Това е съществен момент от изпълнението на функциите и той е особено объркващ за начинаещите програмисти, които си мислят, че написан от тях код след твърдението return ще се изпълни. Това може да се илюстрира, ако във функцията module() добавите един допълнителен ред на две различни места.
1 вариант
float module()
{
float m = real*real + imaginary*imaginary;
cout << "+++++++++++++++++++++++";
return sqrt(m);
}
2 вариант
float module()
{
float m = real*real + imaginary*imaginary;
return sqrt(m);
cout << "+++++++++++++++++++++++";
}
Съотвенто съобщенията на екрана ще са следните:
1 вариант
For
c1, Enter number (format Re, Im): 2,3
+++++++++++++++++++++++Module
of (2,3) = 3.60555
Do
another (y/n)?
2 вариант
For
c1, Enter number (format Re, Im): 2,3
Module
of (2,3) = 3.60555
Do
another (y/n)?
И функциите, които не връщат стойност, могат да имат твърдението return, но ключовата дума не е последвана от някаква стойност или израз, а просто служи за реализиране на логиката на функцията. Например, функцията за делене на дадено комплексно число на друго, която беше оставена за домашна работа в част 11 би изглеждала по този начин:
// divide the complex number by another one (compl_num)
void div(complex_number compl_num)
{
complex_number cn;
cn.real = real;
cn.imaginary = imaginary;
float sqr_mod = compl_num.module();
if (sqr_mod == 0)
{
cout << "Devision by zero!" << endl;
return;
}
sqr_mod *= sqr_mod;
real = (cn.real*compl_num.real + compl_num.imaginary*cn.imaginary)/sqr_mod;
imaginary = (compl_num.real*cn.imaginary - cn.real*compl_num.imaginary)/sqr_mod;
}
При проверката на равенство на модула на комплексното число на нула, ако променливата sqr_mod (която е квадрат на модула на комплексното число, но по време на проверката е модулът) е нула, програмата изписва Devision by zero! и излиза от функцията div(complex_number compl_num). Тук отново една функция на класа, div(complex_number compl_num), извиква друга функция на класа - module().
3. Автоматични променливи и стек. Променливите cn и sqr_mod са така наречените автоматични променливи (Automatic Variables). Те се създават автоматично от програмата когато функцията се извиква и след изпълнението на програмата те се разрушават и освобождават използваната от тях памет. Първата променлива cn се декларира и след това се инициализа със стойност, докато sqr_mod се декларира и инициализира със стойност, която се изчислява от функцията module(). Автоматичните променливи се съхраняват в стека (Stack).
Стекът е част от компютърната памет, в която част се съхраняват променливите на функциите. Стекът нараства и намалява когато функциите се извикват, и съответно, приключват своето изпълнение. Когато автоматична променлива се създаде тя има произволна стойност. Това е така, защото стекът се е разширил и заел ново място от паметта, където е имало нещо съхранено преди това. Затова не очаквайте автоматичните променливи да имат стойност нула, а ги инициализирайте предварително.
Безименни автоматични променливи (Nameless Automatic Variables): във функцията module() двата програмни реда могат да се сбият на един по следния начин:
float module()
{
return sqrt(real*real + imaginary*imaginary);
}
Изчисленията в скобите ще се проведат и присвоят на безименна променлива, която ще бъде коренувана и резултатът ще бъде върнат на променлива от тип float, защото функцията връща такъв тип резултат. Безименната автоматична променлива ще бъде унищожена при излизане от функцията.
4. Програма за аритметични действия с комплексни числа. Пълният код на примерната програма е следният:
//
complex2.cpp
//
a class that models a complex number data type
#include
<iostream.h>
#include
<iomanip.h>
class
complex_number
{
private:
float real; // real
part
float imaginary; // imaginery part
public:
void set()
{
char dummy; // for comma
cout << "Enter number (format Re, Im): ";
cin >> real >> dummy >> imaginary;
}
void display()
{
cout << '('
<< showpoint
<< setprecision(6) << real
// real part
<< ','
<< setprecision(6) << imaginary // imaginary part
<< ')';
}
// add to the complex number another one (compl_num)
void add(complex_number compl_num)
{
real += compl_num.real;
imaginary += compl_num.imaginary;
}
// subtract from the complex number another one (compl_num)
void sub(complex_number compl_num)
{
real -= compl_num.real;
imaginary -= compl_num.imaginary;
}
// multiply the complex number by another one (compl_num)
void mult(complex_number compl_num)
{
complex_number cn;
cn.real = real;
cn.imaginary = imaginary;
real = cn.real*compl_num.real - compl_num.imaginary*cn.imaginary;
imaginary = cn.real*compl_num.imaginary + compl_num.real*cn.imaginary;
}
// divide the complex number by another one (compl_num)
void div(complex_number compl_num)
{
complex_number cn;
cn.real = real;
cn.imaginary = imaginary;
float sqr_mod = compl_num.module();
if (sqr_mod == 0)
{
cout << "Devision by zero!" << endl;
return;
}
sqr_mod *= sqr_mod;
real = (cn.real*compl_num.real + compl_num.imaginary*cn.imaginary)/sqr_mod;
imaginary = (compl_num.real*cn.imaginary - cn.real*compl_num.imaginary)/sqr_mod;
}
// calculate the module of the complex number
float module()
{
float m = real*real + imaginary*imaginary;
return sqrt(m);
}
}; // class complex_number
void
main()
{
complex_number c1, c2; // create two complex_number variables
char choice;
do
{
// enter c1
cout << "For c1, ";
c1.set(); // set c1
// enter c1
cout << "For c2, ";
c2.set(); // set c2
cout << "\nAdd, Subtract, Multiply or Divide (a/s/m/d)? ";
cin >> choice;
switch (choice)
{
// add c2 to c1 and save the result into c1
case 'a':
{
c1.add(c2);
c1.display(); // display
c1
break;
}
// subtract c2 from c1 and save the result into c1
case 's':
{
c1.sub(c2);
c1.display(); // display
c1
break;
}
// multiply c1 by c2 and save the result into c1
case 'm':
{
c1.mult(c2);
c1.display(); // display
c1
break;
}
// divide c1 by c2 and save the result into c1
case 'd':
{
c1.div(c2);
c1.display(); // display
c1
}
} // switch choice
cout << "\nDo another (y/n)? ";
cin >> choice;
} while(choice != 'n');
} //end main
Във функцията display() има два нови манипулатора showpoint и setprecision. Първият принуждава програта да генерира винаги десетична точка за числата с плаваща запетая, а вторият определя точността с която ще се показват тези числа.
В главната функция на програмата се декларират две променливи c1 и c2 от тип complex_number. В един цикъл се вкарват стойностите на двете променливи и потребителят се пита за типа на аритметичното действие, което трябва да се извърши. В конструкцията switch се прави избор коя функция да се изпълни.
5. Връщане на стойност от типа complex_number . Четирите функции за четирите аритметични действия са дефинирани с ключовата дума void и се извикват по следния начин:
c1.add(c2);
c1.sub(c2);
c1.mult(c2);
c1.div(c2);
В първото извикване, комплексното число c2 се прибавя към c1., във второто c2 се изважда от c1 и т.н., като винаги резултатът се съхранява в c1, т.е. c1 се променя. Това е малко объркващо и от самото повикване не може да се разбере кой обект се променя (кой обект съхранява резултата), затова тези функции могат да бъдат пременени, така че да се извикват по следния начин:
c3
= c1.add(c2);
c3
= c1.sub(c2);
c3
= c1.mult(c2);
c3
= c1.div(c2);
Този начин е много по-разбираем и самообяснителен. Ето например дефиницията на функцията mult(c2) в класа complex_number.
//
multiply two complex numbers and assign the result to third one
complex_number
mult(complex_number compl_num)
{
complex_number cn;
cn.real = real*compl_num.real - imaginary*compl_num.imaginary;
cn.imaginary = real*compl_num.imaginary + compl_num.real*imaginary;
return (cn);
}
В този случай cn е автоматична променлива, която съхранява временно резултата. В последния ред тя го предава на функцията. Другите три функции ги напишете сами по аналогия с тази функция. Извикването на функции в главната функция на програмата от т. 4 трябва да бъде променена по следния начин, за да може да се използват:
c3
= c1.add(c2);
c3
= c1.sub(c2);
c3
= c1.mult(c2);
c3
= c1.div(c2);
Опитайте се сами да промените прогарамата от т. 4. Ето един вариант с използване на новия вид на функциите - complex3.cpp
5. Други примери за функции. По традиция ще завършим с две програми, взети от книгата на Лафоор. Те разширяват манипулациите с обектите от класа airtime - вижте примерната програма timecnv1 от предишния материал.
В първата примерна програма, timecnv2.cpp, обект от тип airtime се превръща в обект от тип int с функцията AirtimeToMins.
int AirtimeToMins() // convert airtime to minutes
{
int imins = hours*60 + minutes;
return imins;
}
imins е автоматична променлива, която се създава в стека и се унищожава с прекратяване на изпълнението на функцията. Както споменахме по-горе за безименните автоматични променливи и тук тези два реда в тялото на функцията можем да ги запишем като един:
int AirtimeToMins() // convert airtime to minutes
{
return hours*60 + minutes;
}
Във втория пример, timeret.cpp, се събират две времена (обекта от клас airtime) и сумата се присвоява на трети обект. Съответната функция е:
airtime add(airtime at2)
{
airtime temp;
temp.minutes = minutes + at2.minutes; // add minutes
temp.hours = hours + at2.hours;
// add hours
if(temp.minutes > 59)
// if carry,
{
temp.minutes = temp.minutes - 60; // adjust minutes
temp.hours = temp.hours + 1;
// and hours
}
if(temp.hours > 23)
// if carry,
temp.hours = temp.hours - 24;
// adjust hours
return temp;
}
Разбира се, в тялото на главната функция извикването на функцията add е:
t3 = t1.add(t2); // add t1 and t2, result in t3
С това завършихме увода във функциите на класовете и в следващата лекция ще се занимаем с масивите (arrays) и низовете (strings).
Литература
[1] Robert Lafore; C++ Interactive Course. Waite Group Press, Macmillan Computer Publishing, 1996.
Автор: Проф. Процесор Аритметиков
Keywords: С++,
OOP programming , C++ , Classes , Inheritance , Reusability , Creating
New Data Types , Polymorphism and Overloading
Ключови думи: клас
, обект, обектно ориентирано програмиране , полиморфизъм switch if else
?