План:
Функциите като черни кутии
Писане на функции
Коментари и документиране
Връщане на стойност и оператор return
Параметри на функции
Деклариране на функции
Странични ефекти и процедури
Параметри-псевдоними
Константни псевдоними
Област на действие на променливите, глобални променливи
Постъпково прецизиране, създаване на кода, проиграване
** Функциите като черни кутии
Функция е част от код, определен с име.
До сега сме използвали редица функции, които са предоставени:
-- от системните библиотеки на С++
sqrt(x) - пресмята корен квадратен на числото с
плаваща точка x
getline(cin, s) - чете
един ред от потока cin
-- като член-функции от класа string на Стандартната библиотека
с шаблони на С++
s.length() - връща дължината на низа s
-- като член-функции от потребителските класове Time
и Employee, дефинирани в учебника
t.get_second() - връща секундите от обекта t на класа Time
Функциите могат да се разглеждат като черни кутии, за които
знаем само вход и изход, без да се интересуваме как работи
функцията.
- Изпълнението на main() временно се прекратява докато се изпълнява функцията sqrt.int main()
{ cout << "Enter a positive number: ";
double x;
cin >> x;
x = sqrt(x); // sqrt is running
cout << "The result is " << x << "\n";
return 0;
}
|
|
Член-функция | Неявен параметър | Тип
на неявен параметър |
Явен параметър |
Тип на явен параметър |
harry.get_salary() | harry | Employee | няма |
- |
t.add_seconds(10) | t | Time | 10 |
int |
s.substr(0,num) | s | string | 0, num |
int, string |
Функция | Тип на върнатата стойност |
sqrt(x) | double |
c.substr(0, num) | string |
t.get_seconds() | int |
e.set_salary(s) | no return value |
Функция | Изисквания за
параметрите - тип и lvalue/rvalue |
fabs(w) | Параметърът w е от тип double
- константа, променлива, израз (rvalue) |
t.second_from(t1) | Параметърът t1 е обект от класа Time - константа, променлива |
getline(cin,s) | Първият параметър е обектът cin, вторият
параметър е променлива (lvalue) от тип string |
double future_value(double p)* Функцията се дефинира като се зададе:
{ double b = 1000 * pow(1 + p / 100, 10);
return b;
}
// futval1.cpp
#include <iostream>
#include <cmath>
using namespace std;
// дефиниция на функция
double future_value(double p)
{ double b = 1000 * pow(1 + p / 100, 10);
return b;
}
int main()
{ cout << "Please enter the interest rate in percent: ";
double rate;
cin >> rate;
// извикване на функция
double balance = future_value(rate);
cout << "After 10 years, the balance is "
<< balance << "\n";
return 0;
}
nkirov@cpp % c++ futval1.cpp nkirov@cpp % ./a.out Please enter the interest rate in percent: 8 After 10 years, the balance is 2158.92 |
double future_value(double initial_balance, double p, int n)
{ double b = initial_balance * pow(1 + p / 100, n);
return b;
}
futval.cppdouble balance = future_value(1000, rate, 10);
nkirov@cpp % c++
futval.cpp nkirov@cpp % ./a.out Please enter the interest rate in percent: 12 After 10 years, the balance is 3105.85 |
/**Може да се използва и показания формат за български език.
Computes the value of an investment with compound interest.
@param initial_balance - the initial value of the investment
@param p the interest rate per period in percent
@param n the number of periods the investment is held
@return the balance after n periods
*/
double future_value(double initial_balance, double p, int n)
{ double b = initial_balance * pow(1 + p / 100, n);
return b;
}
Всеки клон на функцията (вследствие на оператор if) трябва да връща някаква стойност.double future_value(double initial_balance, double p, int n)
{ if (n < 0) return 0;
if (p < 0) return 0;
double b = initial_balance * pow(1 + p / 100, n);
return b;
}
Последният оператор на всяка функция би трябвало да бъде оператор return.double future_value(double initial_balance, double p, int n)
{ if (p >= 0)
return initial_balance * pow(1 + p / 100, n);
/* Error */
}
Функция, която връща булева стойност се нарича предикат.bool approx_equal(double x, double y)
{ const double EPSILON = 1E-14;
if (x == 0) return fabs(y) <= EPSILON;
if (y == 0) return fabs(x) <= EPSILON;
return fabs(x - y) / max(fabs(x), fabs(y)) <= EPSILON;
}
// approx.cpp
#include <iostream>
#include <algorithm>
using namespace std;
/**
Tests whether two floating-point numbers are
approximately equal.
@param x a floating-point number
@param y another floating-point number
@return true if x and y are approximately equal
*/
bool approx_equal(double x, double y)
{
const double EPSILON = 1E-14;
if (x == 0) return fabs(y) <= EPSILON;
if (y == 0) return fabs(x) <= EPSILON;
return fabs(x - y) / max(fabs(x), fabs(y)) <= EPSILON;
}
int main()
{
double x;
cout << "Enter a number: ";
cin >> x;
double y;
cout << "Enter another number: ";
cin >> y;
if (approx_equal(x, y))
cout << "The numbers are approximately equal.\n";
else
cout << "The numbers are different.\n";
return 0;
}
nkirov@cpp % c++
approx.cpp nkirov@cpp % ./a.out Enter a number: 1 Enter another number: 1.0000000000000001 The numbers are approximately equal. nkirov@cpp % ./a.out Enter a number: 10 Enter another number: 10.001 The numbers are different. |
* Формални параметри - параметри-променливи, които се задават при дефиниране на функцията.
double future_value(double initial_balance, double p, int n)
* Фактически параметри (аргументи) -
представляват изрази за инициализация на
параметрите-променливи и се задават при извикване на
функцията.
b = future_value(total/2, rate, year2-year1);
Фактическите параметри съответстват по тип и брой на формалните параметри: променливите total и rate са от тип double, а year1 и year2 са от тип int .
Формалните параметри са променливи, които може да се променят
в тялото на функцията.
double future_value(double initial_balance, double p, int n)
{ p = 1 + p / 100;
double b = initial_balance * pow(p, n);
return b;
}
Често това се счита за лош стил на програмиране (bad
programming style).
// printime.cpp
#include <iostream>
#include <iomanip>
using namespace std;
#include "ccc_time.h"
/**
Print a time in the format h:mm:ss
@param t the time to print
*/
void print_time(Time t)
{
cout << t.get_hours() << ":" << setw(2) << setfill('0')
<< t.get_minutes() << ":" << setw(2)
<< t.get_seconds() << setfill(' ');
}
int main()
{
Time liftoff(7, 0, 15);
Time now;
cout << "Liftoff: ";
print_time(liftoff);
cout << "\n";
cout << "Now: ";
print_time(now);
cout << "\n";
return 0;
}
nkirov@cpp % c++
printime.cpp ccc_time.cpp nkirov@cpp % ./a.out Liftoff: 7:00:15 Now: 11:00:00 |
Пример: Функция за увеличаване на заплата.
Процедурата raise_salary увеличава заплатата на служител e с by процента.
raise_salary(harry, 5);
void raise_salary(Employee& e, double by)
{
double new_salary = e.get_salary() * ( 1 + by / 100);
e.set_salary(new_salary);
}
// raisesal.cpp
#include <iostream>
using namespace std;
#include "ccc_empl.h"
/**
Raise an employee salary
@param e employee receiving raise
@param by the percentage of the raise
*/
void raise_salary(Employee& e, double by)
{
double new_salary = e.get_salary() * (1 + by/100);
e.set_salary(new_salary);
}
int main()
{
Employee harry("Hacker, Harry", 45000.00);
raise_salary(harry, 5);
cout << "New salary: " << harry.get_salary() << "\n";
return 0;
}
Формалният параметър Employee& e на функцията raise_salary е параметър-псевдоним на променливата harry от главната функция main.
Промяната на стойността на параметъра-псевдоним e във функцията raise_salary води до промяна и на стойността на фактическия параметър (променливата) harry от функция main.
Параметрите-псевдоними се наричат още входно-изходни параметри - те пренасят информация от извикващата функция към извиканата функция и обратно.
** Константни
псевдоними
*
Използват се за да се избегне копирането - особено нежелателно,
ако параметърът е обект, съдържащ много данни.
* Тъй като стойността на константния псевдоним не може да се
променя, той не "пренася" данни от извиканата функция (print_employee)
в извикващата функция (main).
Пример: Функция ца
отпечатване на данните на обект от тип Employee
void
print_employee(const Employee & e)
{
cout <<
"Name: " << e.get_name()
<< "Salary: " << e.get_salary();
}
int main()
{
Employee harry("Hacker, Harry", 45000.00);
print_employee(harry);
return 0;
}
Параметри-променливи | Параметри-псевдоними | Константни псевдоними |
Входни параметри за функцията | Входно-изходни параметри за функцията | - |
Предаване на параметри чрез копиране | Предаване на параметри по адрес (не се копира) |
Предаване на параметри по адрес (не се копира) |
Стойността на параметъра може да се променя (фактическия
параметър не се променя) |
Стойността на параметъра може да се променя (фактическия
параметър се променя) |
Стойността на параметъра не може да се променя |
Фактически параметър може да бъде константа, променлива
или израз (rvalue) |
Фактически параметър може да бъде само променлива (lvalue) |
Фактически параметър може да бъде константа, променлива
или израз (rvalue) |
** Област на действие на
променливите (scope), глобални променливи
* Ако едно и също име на променлива се използва в
две функции, то не съществува връзка между двете
променливи.
* Областта на действие на променлива, която е дефинирана в блока
на функция, се простира единствено в рамките на тази
функция.
Пример: Сложна лихва - променливи с едно и също име в две
функциии.
// futval1.cpp
#include <iostream>
#include <cmath>
using namespace std;
double
f_val(double i_bal, double p, int ny) /*дефиниции на параметрите-променливи i_bal,
p и ny*/
{
double r; /*дефиниция на променливата r в блока на
функцията future_value*/
r = i_bal*pow(1 + p/(12*100),
12*ny);
return r;
} /*край
на областта на действие на променливите i_bal, p, ny и r*/
int main()
{
cout << "Interest rate in
percent: ";
double r; /*дефиниция на променливата r в блока на
функцията main*/
cin >> r;
double bal = f_val(1000, r,
10); /*дефиниция на
променливата bal*/
cout << "After 10 years,
the balance is " << bal << "\n";
return 0;
} /* край на областта на действие на
променливите r и bal*/
В примера няма връзка между двете
променливи с име r в
двете функции future_value и main.
* Променлива, дефинирана в блок на функция, се нарича локална променлива.
* Областта на действие на локална променлива е от мястото на
дефинирането до края на блока, в който е дефинирана
променливата (block scope).
* В тази област променливата е видима, т.е. може да бъде
използвана.
* Променлива, дефинирана извън
блок на функция, се нарича глобална
променлива.
* Областта на действие на глобална променлива е от мястото на
дефинирането до края на файла, в който е дефинирана
променливата (file scope).
* Досега са използвани глобалните променливи (обекти) cin и cout, които са дефинирани в
заглавния файл iostream.h.
Пример: Увеличаване на заплата с глобална променлина.
// global.cpp
#include <iostream>
using namespace std;
#include "ccc_empl.cpp"
double annual_raise; /* дефиниране на глобална променлива */
void raise_salary(Employee& e)
{
double new_salary = e.get_salary()
*(1 + annual_raise/100); /* използване на глобалната променлива */
e.set_salary(new_salary);
}
int main()
{
Employee boss("Reindeer, Rudolf", 48000.00);
Employee
harry("Hacker, Harry", 35000.00);
annual_raise = 5; /* използване на глобалната променлива */
raise_salary(boss);
raise_salary(harry);
cout <<
boss.get_name() << " " << boss.get_salary()
<< "\n";
cout <<
harry.get_name()<< " " <<
harry.get_salary()<< "\n";
return 0;
}
Reindeer, Rudolf 50400 Hacker, Harry 36750 |
string digit_name(int n)
/* ЦЕЛ: превръща цифра в
английското й име
ПОЛУЧАВА: n - цяло
число между 1 и 9
ВРЪЩА: името на
n ("one" . . . "nine")
*/
{ if (n == 1) return "one";
else if (n == 2)
return "two";
else if (n == 3)
return "three";
else if (n == 4)
return "four";
else if (n == 5)
return "five";
else if (n == 6)
return "six";
else if (n == 7)
return "seven";
else if (n == 8)
return "eight";
else if (n == 9)
return "nine";
return "";
}
string teen_name(int n)
/* ЦЕЛ: превръща цяло число
между 10 и 19 в английското му име
ПОЛУЧАВА: n - цяло
число между 10 и 19
ВРЪЩА: името на
n ("ten" . . . "nineteen")
*/
{ if (n == 10) return "ten";
else if (n == 11)
return "eleven";
else if (n == 12)
return "twelve";
else if (n == 13)
return "thirteen";
else if (n == 14)
return "fourteen";
else if (n == 15)
return "fifteen";
else if (n == 16)
return "sixteen";
else if (n == 17)
return "seventeen";
else if (n == 18)
return "eighteen";
else if (n == 19)
return "nineteen";
return "";
}
string tens_name(int n)
/* ЦЕЛ: дава английското име
на десетиците между 20 и 90
ПОЛУЧАВА: n - цяло
число между 2 и 9
ВРЪЩА: името на
10*n ("twenty" . . . "ninety")
*/
{ if (n == 2) return
"twenty";
else if (n == 3)
return "thirty";
else if (n == 4)
return "forty";
else if (n == 5)
return "fifty";
else if (n == 6)
return "sixty";
else if (n == 7)
return "seventy";
else if (n == 8)
return "eighty";
else if (n == 9)
return "ninety";
return "";
}
string int_name(int n)
/* ЦЕЛ: превръща цяло число в
английското му име
ПОЛУЧАВА: n - цяло
положително число < 1000000
ВРЪЩА: името на
n (т.е. "two hundred seventy four")
*/
{ int c = n; /* оставащата за превръщане част */
string r; /* стойността на функцията */
if (c >= 1000)
{ r = int_name(c
/ 1000) + " thousand";
c =
c % 1000;
}
if (c >= 100)
{ r = r + " " +
digit_name(c / 100) + " hundred";
c =
c % 100;
}
if (c >= 20)
{ r = r + " " +
tens_name(c / 10);
c =
c % 10;
}
else if (c >= 10)
{ r = r + " " +
teen_name(c);
c =
0;
}
if (c > 0)
r =
r + " " + digit_name(c);
return r;
}
int main()
{ int n;
cout << "Please
enter a positive integer: ";
cin >> n;
cout <<
int_name(n);
return 0;
}
Please enter a
positive integer: 20119 twenty thousand one hundred nineteen |