Замяна на функции
или т.н.
Презаредени функции
(Overloaded Functions)
Замяната на функции или т.н. презаредени функции е едно превъзходно удобство в C++. Тази концепция позволява да се използва едно и също име за различни функции, чрез различаването им чрез набора от параметри, с които функциите се извикват.
1. Необходимост от презаредени функции (Need for Function Overloading). Нека разгледаме следния фрагмент от C код, който използва две функции за абсолютна стойност от стандартната библиотека на C (C standard library ).
int a = -2;
double b = -3.14;
cout << abs(a) << endl;
cout << abs(b) << endl;
cout << fabs(b) << endl;
Както и очаквахме предпоследният ред дава грешен резултат (отрязана е дробната част на числото b) - вижте изходът по-долу.
2
3
3.14
За съжаление в C за абсолютна стойност има няколко функции, които оперират с различни аргументи. Функцията abs() работи с целочислени стойности, а функцията fabs() - има аргументи, които са числа с плаваща запетая. (Има и специална функция cabs(), която връща абсолютната стойност (модула) на комплексно число.) Декларацията на тези няколко функции (и още една - fabsl) в хедър файла math.h е следната:
int
abs(int x);
double
fabs(double x);
long
double fabsl(long double x);
double
cabs(struct complex z);
Това използване на няколко функции прави програмистките грешки вероятни и е в пълно противоречие с дефиницията на абсолютна стойност в математиката. Както е известно от математиката абсолютна стойност на едно реално число (целочислените също са реални числа) е противоположното число, ако числото е отрицателно, или същото число, ако числото е положително. Т.е.
м -x
за x < 0
|x| = н
о
x за x >= 0
Също така, използването на няколко функции (това се нарича name proliferation - разпространение на имената) не само способства за повече програмистки грешки, но затруднява програмиста, който трябва да помни и да се съобразява с различните имена. Допълнително, описването на тези няколко функции в помощния файл или в ръководството ще заеме повече място и труд.
Много по-удобно е
използването на презаредени функции, които да са дефинирани с едно и също
име (например Abs),
но които работят с различни по тип параметри и връщат различни по тип резултати.
Например предният пример може да бъде решен по следния начин със следната
кратка програма:
//
abs_ovlo.cpp
//
function overloading: applied for absolte value function
#include
<iostream.h> //
for <<
#include
<conio.h>
// for getch();
int
Abs(int x)
{
if (x < 0)
return -x;
else
return x;
};
double
Abs(double x)
{
if (x < 0)
return -x;
else
return x;
};
long
double Abs(long double x)
{
if (x < 0)
return -x;
else
return x;
};
void
main()
{
int a = -2;
double b = -3.14;
long double c = 2.1234567890123456789E4931L;
// call with int argument
cout << Abs(a) << endl;
// call with double argument
cout << Abs(b) << endl;
// call with long double argument
cout << Abs(c) << endl;
getch();
}
// main()
Виждаме, че функцията за намиране на модул на едно число, Abs(), е дефинирана три пъти, като всеки път тя има различен тип на аргумента. Именно този различен тип позволява на компилатора да различава различните дефиниции и да знае коя точно да използва при извикване на функцията. При първото извикване тя се използва с целочислен аргумент, при второто с такъв с двойна точност и накрая с аргумент с най-голяма точност (long double).
! Типът на резултата, който се връща от функцията не може да определя дали функцията е презаредена. В горния случай той е различен в трите случая, защото е еднакъв с типа на аргумента, но това е необходимо поради логиката в тази математическа операция.
2. Декларация на презаредени функции. Както видяхме С++ дава възможност няколко функции да имат едно и също име, но различен брой и тип аргументи и когато биват извиквани да знаят коя от тях ще се изпълни в зависимост от типа на параметрите, с които се извиква функцията. Но как компилаторът разпознава, например в горната кратка програма, че това са две различни функции и коя от тях трябва да бъде използвана? За да "наименова" дадена функция компилаторът гледа не само името на функцията, но и типа и броя на параметрите в нея. Това се нарича name mangling - изопачаване на името [на функцията]. Точното пълно име варира от компилатор на компилатор, но без да влизаме в подробности межем да си представим, че първата функция има име (за компилатора) Abs_int(), а втората - Abs_double().
!Обърнете внимание, че промяната само на типа на връщаната стойност не прави презаредени функции. Т.е. тези две декларации по-долу, които се различават по типа на връщаната стойност не са презаредени функции и компилаторът на Борланд Билдер дава съобщението "Type mismatch in redeclaration of 'Abs(int)'."
int
Abs(int x);
double
Abs(int x); // not an overloaded function!
3. Друг пример
за презаредени функции (A Member Function Example). Този пример е взет
от превъзходния курс на Лафоор [1], В кратката програма
по-долу има дефинирани три функции на класа с едно и също име. Целта е
класът да показва текст по три различни начини.
//
overfun.cpp
//
overloaded functions
#include
<iostream.h>
#include
<string.h>
// for strcpy()
#include
<conio.h>
// for getch()
const int MAX_LENGTH = 40; // maximum length of text
class
fancy_text
// class displays text
{
private:
char text[MAX_LENGTH]; // text
to be displayed
public:
void set_text(char tx[]) // set the text
{
strcpy(text, tx);
}
void box_display(); // line of dashes
void box_display(char); // line of characters
void box_display(char, int); // line of n characters
};
void
fancy_text::box_display() // line of 40 dashes
{
cout << "----------------------------------------";
cout << endl << text << endl;
cout << "----------------------------------------";
}
//
line of 40 characters
void
fancy_text::box_display(char ch)
{
int j;
for (j = 0; j < MAX_LENGTH; j++)
cout << ch;
cout << endl << text << endl;
for (j = 0; j < MAX_LENGTH; j++)
cout << ch;
}
// line of n characters
void
fancy_text::box_display(char ch, int n)
{
int j;
for (j = 0; j < n; j++)
cout << ch;
cout << endl << text << endl;
for (j = 0; j < n; j++)
cout << ch;
}
void
main()
{
fancy_text ft1;
ft1.set_text("Gone with the Wind");
ft1.box_display();
// display text with default lines
cout << endl << endl;
ft1.box_display('+');
// default length and equal signs
cout << endl << endl;
ft1.box_display('=', 18); //
equal signs; length matches text
cout << endl << endl;
getch();
} // main()
Изходът от програмата е следният:
----------------------------------------
Gone
with the Wind
----------------------------------------
++++++++++++++++++++++++++++++++++++++++
Gone
with the Wind
++++++++++++++++++++++++++++++++++++++++
==================
Gone
with the Wind
==================
Трите презаредени функции са съответно void box_display(), void box_display(char ch) и void box_display(char ch, int n). Функциите са дефинирани извън класа - вижте използването на оператора за за решение за обхвата ::. Първата функция отпечатва текста от променливата на класа char text[MAX_LENGTH], заградена от два реда от тирета с дължина 40 символа, втората позволява избиране на символ за заграждащите редове (в случая това е е знакът плюс, който е предаден като аргумент на параметъра на функцията ch), а третата презаредена функция позволява не само избор на символ за, но и дължината на, заграждащите редове. В програмата третата функция е извикана по следния начин ft1.box_display('=', 18);, което отпечатва два заграждащи реда с дължина от 18 символа и със символи знака равно.
Обърнете внимание, че едно грешно извикване като това по-долу, би дало крайно учудващи резултати. Компилаторът не издава предупреждение и тълкува числото 18 като 18-тия поред символ в кодовата таблица и изписва два реда с него.
ft1.box_display(18);
// erroneous call !!!
cout << endl << endl;
В следващата лекция ще се занимаем с т.н. аргументи по подразбиране (Default Arguments), които също ни спестяват програмен код и улесняват програмистите.
Автор: Проф. Процесор Аритметиков
Keywords: С++,
OOP programming , C++ , Classes , Inheritance , Reusability , Creating
New Data Types , Polymorphism and Overloading
Ключови думи: клас
, обект, обектно ориентирано програмиране , полиморфизъм switch if else
?