Ֆունկցիաներ
Մաթեմատիկական մեկնաբանությամբ ֆունկցիան մի գործողություն է, որը մի բազմությունը՝ որոշման տիրույթը, արտապատկերում է մեկ այլ բազմության՝ արժեքների տիրույթին։ C++ լեզվում նույնպես այդպես է․ ֆունկցիան ստանում է արգումենտներ և վերադարձնում է արժեք։ Բայց նաև, ծրագրավորման գործի առանձնահատկություններից ելնելով, C++ լեզուն թույլատրում է սահմանել ֆունկցիաներ, որոնք արգումենտներ չեն ստանում կամ/և արժեքներ չեն վերադարձնում։ Այլ կերպ ասած, C++ լեզվում ֆունկցիան նաև պրոցեդուրա է, որը կարող է թողնել հաշվարկման կողմնակի էֆեկտներ։
Նախորդ գլուխներում main
ֆունկցիայի օրինակով արդեն տեսանք, որ C++ լեզվում ֆունկցիայի սահմանումն ունի չորս բաղադրիչ. ա․ վերադարձվող արժեքի տիպ, բ․ անուն, գ․ պարամետրերի ցուցակ, դ․ մարմին։
Ֆիզիկայի դպրոցական դասընթացից հիշենք ազատ անկում կատարող մարմնի անցած ճանապարհի բանաձևը․ (որտեղ ) և սահմանենք դրան համարժեք C++ ֆունկցիան․
double distance( double t )
{
return (9.81 * t * t) / 2;
}
Այս ֆունկցիայի վերադարձվող արժեքի տիպը double
է, անունը distance
է, պարամետրերի ցուցակում double
տիպի t
պարամետրն է, իսկ մարմինը բաղկացած է միակ return
հրամանից, որի արգումենտում մարմնի անցած ճանապարհի հաշվման բանաձևն է։
Ֆունկցիայի տիպ է կոչվում վերադարձրած արժեքի տիպի ու պարամետրերի տիպերի ցուցակի համադրությունը։ Մաթեմատիկական արտահայտությամբ, օրինակ, distance
ֆունկցիան իրական թվերի բազմությունն արտապատկերում է նույն իրական թվերի բազմությանը՝ ։
Եթե հարկավոր է սահմանել այնպիսի մի ֆունկցիա, որից արժեք չենք ակնկալում, ապա պետք է դրա վերադարձվող արժեքի տիպը նշել որպես void
։ Օրինակ, ստորև սահմանված print_number
ֆունկցիան ստանում է առանց նշանի ամբողջ թիվ (unsigned int
) և արտածում է այդ թվի տասական, ութական ու տասնվեցական ներկայացումները։ Այդ ներկայացումները ստացվում են հոսքերի dec
, oct
և hex
մանիպուլյատորների օգնությամբ, որոնք սահմանված են iostream
ֆայլում։
void print_number( unsigned int num )
{
std::cout << std::dec << num << " "
<< std::oct << num << " "
<< std::hex << num << " "
<< std::endl;
}
Եթե ֆունկցիայի պարամետրերի քանակը մեկից ավելի է, ապա դրանք պարամետրերի ցուցակում թվարկվում են իրարից ստորակետով անջատված։ Օրինակ, avg
ֆունկցիան ստանում է երեք իրական թիվ և վերադարձնում է դրանց թվաբանական միջինը։
double avg( double a, double b, double c )
{
auto sum = a + b + c;
return sum / 3;
}
Եթե մի որևէ f()
ֆունկցիայում օգտագործվելու է մի այլ g()
ֆունկցիա, ապա այն պետք է ավելի շուտ հայտարարված կամ սահմանված լինի։ Օրինակ, ծրագրի տեքստում f()
և g()
ֆունկցիաների փոխադարարձ դասավորությունը կարող է լինել այսպիսին.
int g( double a, char b )
{
// ինչ-որ հրամաններ
return 777;
}
void f( int x )
{
// ինչ-որ հրամաններ
auto v = g( 3.14, 'p' );
// ինչ-որ հրամաններ
}
Կամ, f()
ֆունկցիային կարող է նախորդել միայն g()
ֆունկցիայի հայտարարությունը։ Ահա այսպես.
int g( double, char );
void f( int x )
{
// ինչ-որ հրամաններ
auto v = g( 3.14, 'p' );
// ինչ-որ հրամաններ
}
int g( double a, char b )
{
// ինչ-որ հրամաններ
return 777;
}
Առաջին տողում գրված արտահայտությունը կոմպիլյատորին տեղեկացնում է, որ գոյություն ունի g
անունով ֆունկցիա՝ double
և char
տիպի պարամետրերով և int
արժեք վերադարձնող, որի սահմանումը գալու է քիչ ավելի ուշ։
C++ լեզուն թույլ է տալիս սահմանել նույն անունով, բայց տարբերվող պարամետրերի ցուցակով ֆունկցիաներ։ Այդ հնարավորությունն անվանում են ֆունկցիայի վերասահմանում։ Օրինակ, սահմանենք sum
անունով երկու ֆունկցիա, որոնցից առաջինը վերադարձնում է տրված երկու իրական թվերի գումարը, իսկ երկրորդը՝ երեք թվերի գումարը։
double sum( double a, double b )
{
return a + b;
}
double sum( double a, double b, double c )
{
return sum( sum(a, b), c );
}
Վերասահմանվող ֆունկցիաները պետք է տարբերվեն կամ պարամետրերի քանակով, կամ էլ դրանց տիպերով։ Օրինակ, սահմանենք print
անունով երկու ֆունկցիա, որոնցից առաջինը տպում է ամբողջ թիվ, իսկ երկրորդը՝ բուլյան արժեք.
void print( int val )
{
std::cout << "Ես ամբողջ թիվ եմ - "
<< val << std::endl;
}
void print( bool val )
{
std::cout << "Ես բուլյան արժեք եմ - "
<< std::boolalpha << val
<< std::endl;
}
Եթե նույնն սահմանված ֆունկցիաների և անունները, և պարամետրերի քնակները, և դրանց տիպերը, ապա ֆունկցիայի կանչի ժամանակ հնարավոր չի լինի պարզել, թե տարբերակներից որ մեկն է կանչվում։ Կոմպիլյատորը հեշտությոմբ բացահայտում է այսպիսի դեպքերը և ծրագրավորողին տալիս է համապատասխան հաղորդագրություն։
Պարզ է արդեն, որ ֆունկցիայի վերասահմանումը հարմար է այն դեպքում, երբ անհրաժեշտ է ծրագրում ունենալ նույն անունով, բայց մարմինների տարբեր իրականացումներով ենթածրագրեր։ Բայց հետաքրքիր է, որ շատ հաճախ հանդիպում է նաև հակառակ իրավիճակը․ երբ անհրաժեշտ ենթածրագրի նույն իրականացումն օգտագործել տվյալների տարբեր տիպերի համար։
C++ լեզուն հնարավորություն է տալիս սահմանել պարամետրիզացված տիպերով ենթածրագրեր՝ ֆունկցիաների կաղապարներ (template)։ Կաղապարի օգնությամբ սահմանվում է ոչ թե կոնկրետ ֆունկցիան, այլ նրա ընդհանրացված (generic) ներկայացումը։ Կաղապարի սահմանումը սկսվում է template
ծառայողական բառով, որին հետևում են կաղապարի պարամետրերը՝ նշված typename
բառով։ Օրինակ,
template<typename E>
E plus( E x, E y )
{
return x + y;
}
C++ լեզվի ֆունկցիաները կարող են ունենալ նաև լռելության արժեքով պարամետրեր։ Սա նշանակում է, որ ֆունկցիայի սահմանման ժամանակ կարելի է պարամետրերից որոշներին (կամ բոլորին) տալ ինչ-որ արժեքներ, իսկ ֆունկցիայի կանչի ժամանակ, հնարավորության դեպքում, այդ արգումենտները չoգտագործել։ Օրինակ, ենթադրենք սահմանված է հետհևյալ ֆունկցիան.
void hello( std::string name = "աշխարհ" )
{
std::cout << "Ողջո՜ւյն, " << name << "։" << std::endl;
}
Այս դեպքում, եթե այն կանչված է առանց արգումենտների՝ hello()
, ապա արտածվում է «Ողջո՜ւյն, աշխարհ։
» տողը, իսկ եթե արգումենտով, օրինակ, hello("Պետրոս")
, ապա՝ «Ողջո՜ւյն, Պետրոս։
» տողը։
--- Հղումների մասին --- պարամետրերով վերադարձվող արժեքների մասին ---