Програмен език C++
Част 23
.
Отново за функциите
..
(съдържание)
.
В следващите няколко лекции ще се занимаем по-подробно с функции: досега в две лекции бяха разгледани някои техни аспекти - в лекция 5 бяха въведени функциите на класа, в лекция 11 се запознахме с аргументите на функциите и в лекция 14 - връщането на стойност от тях. В настоящата лекция ще систематизираме знанията за функциите, придоби досега.

1. Типове функции, срещани досега. Първо бяха разгледани така наречените функции на класовете (member functions). След това бяха въведени функциите, които са извън класовете. Също така беше наблегнато на главната функция в програмата, която е задължителна за всяка една програма на C++.

Третият вид функции, с които се запознахме досега, са така наречените библиотечни функции (library functions). В повечето представени програми присъстваше функцията getch(), която чете единичен символ от клавиатурата и беше използвана за спиране на изпълнението на програмата, за да се види резултата преди Уиндоус да затвори ДОС-овския прозорец (конзолата). Аналогична на нея функция е getche(), която допълнително изпраща вкарания символ на екрана (echo it): за да можем да работим с тези две функции е необходим заглавния файл CONIO.H. Други библиотечни функции, с които се запознахме, са функции за работа със стрингове (низове) - в част 19 бяха въведени няколко от тях: strcpy(dest, src), strlen(str), strcat(str1, str2), strcmp(str1, str2) и stricmp(str1, str2).

2. Отделните аспекти на функциите. В тази част ще бъдат характеризирани отделните аспекти (характеристики) на функциите и тяхното извикване.

2.1. Извикване на функциите (Function Calls). На практика функцията е част от програмния код, която се извиква за изпълнение от някакво твърдение (statement) в програмата или друга нейна част. (Обикновено когато част от програмния код се среща на няколко места в програмата той се обособява във вид на функция.) Това извикване на функцията предизвиква изпълнението на програмата да "скочи" в началото на функцията, операторите и твърденията в нея да се изпълнят и накрая управлението на програмата да се върне на твърдението, непосредствено след това, което извиква функцията. Това извикване може да е съпроводено с предаване на стойности на някои променливи или константи към изпълняваните твърдения във функцията - така наречените аргументи, както и с връщане на стойност от функцията. Ето един пример (част 5) за извикване на функция на класа без аргументи:

stand1.SoldOneDog();

Първото име е името на обекта, за който се извиква тази функция, следва т.н. дот-оператор и след него името на функцията и двете скоби, които не заграждат нищо. Извикването, като всяко едно твърдение, завършва с точка и запетая.

Ето и пример за извикване на функция с аргументи от лекция 11:

c1.add(c2);

Отново е името на обекта (в случая първото комплексно число), името на функцията и този път името на аргумента (второто комплексно число, което се добавя към първото), заграден в скоби.
.
2.2. Дефиниция на функциите (Function Definitions). Дефиницията на една функция започва с така наречения декларатор (declarator), който определя името на функцията, имената и типа на нейните аргументи и типа на връщаната стойност от функцията. Аргументите са заградени в скобите и разделени със запетайки. След декларатора няма точка и запетая. В големи скоби е заградено тялото на функцията (function body), което се състои от програмни твърдения. Ето един пример на дефиниция на функция от лекция 14:

 // multiply two complex numbers and assign the result to third one
 complex_number mult(complex_number compl_num)       // declarator
 {
   complex_number cn;                                //  first line of function body

   cn.real = real*compl_num.real - imaginary*compl_num.imaginary;
   cn.imaginary = real*compl_num.imaginary + compl_num.real*imaginary;

   return (cn);
 }
.
Типът, който се връща от функцията е класът complex_number. Единственият аргумент на функцията compl_num има същия тип. Във функцията има четири програмни реда - първият дефинира една автоматична променлива cn от типа compl_num, следващите две твърдения извършват аритметични действия, а последният ред връща стойността от изчисленията във функцията (в случая умножението на две комплексни числа). След последната (затваряща) голяма скоба няма точка и запетая - това е различието от специфицирането на класове, например.

2.3. Аргументите (Arguments). Те са начин да бъде предадена информация от мястото на извикване на функцията към тялото на функцията. Строго погледнато, аргументи се наричат стойностите на променливите или константите, които се намират в извикването на функцията, докато променливите в дефиницията на функцията се наричат параметри (parameters), но в разговорния език и двете пронятия се наричат аргументи.

2.4. Връщаната стойност (Return Values). Функцията може да връща стойност към твърдението, което я извиква. В примера, даден по-горе, функцията връща стойност от тип compl_num. Съответно извикването на функцията е следното:

c3 = c1.add(c2);

при което двете комплексни числа c1 и c2 се събират и тяхната сума се присвоява на третото комплексно число c3.

Важна особеност при връщането на стойности от функциите е твърдението return. Много често начинаещите програмисти забравят да го изпишат и даже компилаторът да предупреди той компилира кода и се получава една трудна за откриване логическа грешка. Както видяхме в предишните лекции след return може да стои променлива или израз - в последния случай става въпрос за така наречените безименни автоматични променливи (Nameless Automatic Variables).

В лекция 14 беше споменато, че и функциите, които не връщат стойност, могат да имат твърдението return, но ключовата дума не е последвана от някаква стойност или израз, а просто служи за реализиране на логиката на функцията.

2.5. Декларация на функциите (Function Declarations). Досега всички разглеждани от нас функции бяха дефинирани преди тяхното повикване. За да може компилаторът да включи едно повикване на функция в програмния код той трябва да знае името на функцията, типа на аргументите на функцията и типа на връщания резултат. Когато функцията е дефинирана преди повикването това не е проблем. Но има ситуации, когато например един клас използва функции, чиито дефиниции са след класа. В този случай преди повикването на функцията е необходимо тя да бъде декларирана, чрез изписване на името на функцията, имената и типа на аргументите на функцията и типа на връщания резултат. Ето един пример за декларация.

complex_number mult(complex_number compl_num);  // function declaration or prototype

Забележете точката и запетая след последната скоба - при дефиницията те липсват, защото следва отваряща голяма скоба, а декларацията е просто едно твърдение (statement), а твърденията се завършват с точка и запетая. Декларацията на функциите се нарича още прототип (prototype), защото тя създава един образец, който показва как функцията ще бъде извикана. Разбира се, след декларацията на функция трябва да се намира в кода и дефиницията на функцията. В декларацията имената на аргументите могат да се изпуснат и да се напишат само типовете - например в следния код липсва compl_num:

complex_number mult(complex_number);  // function declaration or prototype

Разбира се, при повече аргументи за програмиста ще е трудно да разбере за какво става въпрос и затова това изпускане на имената на аргументите не се препоръчва.

3. Функции, извиквани от главната функция (Functions Called from main()). Когато главната функция стане много голяма и/или в нея се появи повтарящ се код, то част от нея се изнася като функция или няколко функции. Ако дефиницията на тази функция е отново в главната функция то няма да получим достатъчно съкращение и упростяване на кода на главната функция, затова декларациите се изписват обикновено преди главната функция, а след нея се пишат само дефинициите на функциите, така както е дадено по-долу.

void func1();      // function declarations
void func2();

void main()
{
  func1();        // call to func1()
  func2();        // call to func2()
}

void func1()       // definition of func1()
{
  // statements here
}

void func2()       // definition of func2()
{
  // statements here
}

Разбира се, двете декларации могат да се запишат и в главната функция, която ще изглежда тогава така:

void main()
{
  void func1();      // function declarations
  void func2();
  func1();           // call to func1()
  func2();           // call to func2()
 }

4. Функции, извиквани от функции на класа (Functions Called from Member Functions). Когато функция на класа извиква друга функция, написана от програмиста, то последната може да се декларира в тялото на първата функция и дефиницията да се изнесе извън класа. Ето един примерен код:

class alpha
{
  // other statements
  void member_func()  // member function
  {
    void afunc();   // declaration of afunc()
    // other statements
    afunc();        // call to afunc()
    // other statements
  }
  // other statements
};   // end of class definition

void afunc()         // definition of afunc()
{
  // statements of afunc()
}

С тази лекция бяха систематизирани отделните аспекти на функциите, които бяха разпръснати в предишните лекции. В следващите лекции ще се занимаем с някои нови аспекти на функциите.

.
(съдържание)
.
Литература
.
[1] Robert Lafore; C++ Interactive Course. Waite Group Press, Macmillan Computer Publishing, 1996.

Автор: Проф. Процесор Аритметиков

.
[ това е материал от брой 24 от септември 2008 г. на списание "Коснос" www.kosnos.com ]

Keywords: С++,  OOP programming , C++ , Classes , Inheritance , Reusability , Creating New Data Types , Polymorphism and Overloading
Ключови думи: клас , обект, обектно ориентирано програмиране , полиморфизъм switch if else ?