airtime operator + (airtime right)
{
// make a temporary object
airtime temp;
// add data
temp.hours = hours + right.hours;
temp.minutes = minutes + right.minutes;
// check for carry
if (temp.minutes >= 60)
{
temp.hours++;
temp.minutes -= 60;
}
return temp; // return
temporary object
}
и се извиква по следния начин с три обекта от класа airtime:
at3 = at1 + at2;
Обектът at2 става аргумент на тази функция, а изразът "at1 + " може да се разглежда като извикване на функцията-оператор от обекта at1 по следният начин:
t1.operator+()
т.е. функцията-оператор се извиква за обекта, който се намира в ляво от оператора +. Резултатът, който връща функцията и който е от типа на класа airtime, се присвоява на третия обект at3.
В т. 6 на предишната лекция, в програмата complex4.cpp, ви показахме как се записват другите три аритметични бинарни оператора, които извършват аритиметичните действия изваждане, умножение и делене на комплексни числа. Както се вижда от програмата, има голяма аналогия между техния запис - само се сменя името на оператора и се пренаписват съответните действия във функцията. Така е и с останалите бинарни оператори, като допълнително в някои от случаите се сменя и типа на връщаната стойност.
2. Презареждане на оператори за сравняване (Overloading Relational Operators). В Таблица 1 на лекция 6 бяха дадени операторите за сравняване и тяхното значение при сравняване на различни стойности. Резултатът, който връщат е от тип bool, при който стойността нула има смисъл на лъжа, а всяка една стойност, различна от нула на истина - за този тип данни вижте лекция 22. Ако искаме да презаредим оператора по-малко, <, то схематичният запис на функцията оператор ще е следният:
bool operator < (SomeClass arg_obj)
{
// needs function body
}
като какво точно ще стои в тялото на тази функция, зависи от това, какво сравняваме в обектите на класа SomeClass.
Ето една примерна програма от курса на Лафоор [1], която сравнява обекти от класа airtime.
//
compair.cpp
//
overloads the < operator
#include
<iostream.h>
#include
<conio.h>
// for getch()
class
airtime
{
private:
int hours;
// 0 to 23
int minutes;
// 0 to 59
public:
// output to screen
void display() const
{
cout << hours << ':' << minutes;
}
// input from user
void get()
{
char dummy;
cout << "\nEnter time (format 12:59): ";
cin >> hours >> dummy >> minutes;
}
// overloaded < operator
bool operator < (const airtime & right)
{
if (hours < right.hours)
return true;
if (hours == right.hours && minutes < right.minutes)
return true;
return false;
}
};
// end class airtime
void
main()
{
airtime at1, at2;
cout << "Enter first airtime: ";
at1.get();
cout << "Enter second airtime: ";
at2.get();
if (at1 < at2)
cout << "\nfirst less than second";
else
cout << "\nfirst not less than second";
getch();
}
// main()
Логиката на функцията-оператор operator<() не е трудна за разбиране. Първо се сравнява часа на двата обекта, а ако той е еднакъв, след това минутите. Обърнете внимание, че твърдението return прекъсва изпълнението на функцията, т.е. твърденията в тялото на функцията след него не се изпълняват. При извикване на функцията по следният начин if(at1 < at2), се извиква функцията-оператор за обекта, който е от ляво на оператора, т.е. за обекта at1, като аргумент на функцията е обектът at2. Резултатът е от тип bool и се използва в оператора if.
Не е трудно да бъдат написани и функциите-оператори за останалите пет оператора за сравнение от Таблица 1 на лекция 6. Те са дадени по-долу:
// overloaded > operator
bool operator > (const airtime & right)
{
if (hours > right.hours)
return true;
if (hours == right.hours && minutes > right.minutes)
return true;
return false;
}
// overloaded <= operator
bool operator <= (const airtime & right)
{
if (hours <= right.hours)
return true;
if (hours == right.hours && minutes <= right.minutes)
return true;
return false;
}
// overloaded >= operator
bool operator >= (const airtime & right)
{
if (hours >= right.hours)
return true;
if (hours == right.hours && minutes >= right.minutes)
return true;
return false;
}
// overloaded == operator
bool operator == (const airtime & right)
{
if (hours == right.hours && minutes == right.minutes)
return true;
return false;
}
// overloaded != operator
bool operator != (const airtime & right)
{
if (hours == right.hours && minutes == right.minutes)
return false;
return true;
}
Обърнете внимание, че в първите три оператора е сменен само оператора по-малко със съответния оператор. А, ако сравните последните два оператора, ще видите, че местата на true и false са разменени. Ето цялата програма all_comp_air.cpp, която сравнява обекти от класа airtime с всичките шест оператора за сравнение.
3. Постоянни аргументи по връзка (Passing the Argument by const Reference). В шестте функции-оператори в програмата all_comp_air.cpp единственият аргумент се предава по връзка, за да не бавим програмата с копиращия конструктор, който иначе трябва да се извика, ако се предава аргументът по стойност. Но също така, за да не променим случайно този обект-аргумент сме поставили ключовата дума const, пред него. Това не е непозната за нас конструкция - вече я използвахме в конструкторите в лекция лекция 37.
Друго, което е интересно в програмата all_comp_air.cpp е, че няма изрично написан от нас конструктор, както в предните програми от предишната лекция. Той не е необходим, защото не връщаме обект от същия клас, а типът на връщане е предефиниран - bool.
4. Презареждане
на операторите за присвояване +=,
-=,
*=,
/=
(Overloading Assignment Operators).
При тези оператори се променя стойността на левият операнд, като съответно
към него се добавя или изважда стойността на десния, или левият се умножава
или дели на тази стойност.
!
Преди
да разгледаме презареждането на тези оператори ще направим едно отклонение,
за да покажем че операцията присвояване връща стойност, която е равна на
стойността, която е присвоена. Точно затова в C++ (а и в C е така) една
от най-неприятните логически грешки е да запишете
if (x = y) // with the assignment operator
{
// do something
}
Вместо
if (x == y)
{
// do something
}
При първия, грешен запис, стойността на y се присвоява на x и целият израз заема (връща) стойност, равна на стойността на y. Което ще рече, че ако y е различно от нула, ще се изпълнят операторите в блока след if(x = y), а ако то е равно на нула, няма да се изпълнят тези оператори - а това определено не е това, което искате. Разбира се, компилаторът компилира програмата при тази грешка, но предупреждава със съобщението:
[C++ Warning]: W8060 Possibly incorrect assignment.
Именно връщането на стойност от присвояването позволява да напишем съвсем законно в C++ (и C)
Z = x = y
което прави следното: стойността на y се присвоява на x, а самото присвояване получава стойност, равна на тази на y, която стойност се присвоява на z. Така трите числа стават равни едно на друго. Същото е с операторите +=, -=, *= и /=. Това може лесно да се разбере с примерната програма assign_test.cpp.
Сега за презареждането на операторите за присвояване. Ще искаме не само да можем да ги използваме по следния начин
at1 += at2;
но и чрез двойното присвояване
at3 = at1 += at2;
Затова тези оператори трябва да връщат стойност от типа на класа, чийто обекти са операнди на оператора. Ето една примерна програма от курса на Лафоор [1], която презарежда оператора += за да работи с обекти от класа airtime.
//
pleqair.cpp
//
overloads the += operator
#include
<iostream.h>
#include
<conio.h>
// for getch()
#include
<iomanip.h>
// for setw() & setfill()
class
airtime
{
private:
int hours;
// 0 to 23
int minutes;
// 0 to 59
public:
// no-arg constructor
airtime() : hours(0), minutes(0)
{ }
// 2-arg constructor
airtime(int h, int m) : hours(h), minutes(m)
{ }
// output to screen
void display() const
{
cout << hours << ':' << setw(2) << setfill('0')
<< minutes;
}
// input from user
void get()
{
char dummy;
cout << "\nEnter time (format 12:59): ";
cin >> hours >> dummy >> minutes;
}
// overloaded += operator
airtime operator += (const airtime & right)
{
// add argument to us
hours += right.hours;
minutes += right.minutes;
// check for carry
if (minutes >= 60)
{ hours++; minutes -= 60; }
return airtime(hours, minutes); // return our new value
}
};
// end class airtime
void
main()
{
airtime at1, at2, at3;
cout << "Enter first airtime: ";
at1.get();
cout << "Enter second airtime: ";
at2.get();
at1 += at2;
// overloaded += operator
// adds at2 to at1
cout << "\nat1 += at2 = ";
at1.display();
// display result
at3 = at1 += at2; // do it again, use return value
cout << "\nat1 (at3 = at1 += at2;) = ";
at1.display();
// display result
cout << "\nat3 (at3 = at1 += at2;) = ";
at3.display();
// display result
getch();
}
// main()
Във функцията-оператор operator+= има няколка ключови момета. Първо, обектът, който стои отдясно на оператора (в главната функция в първото присвояване той е at2) не се променя и затова аргументът е постоянен, въпреки че се предава по връзка. Второ, самият обект, който се намира в лявата страна на оператора +=, се променя и това е отразено в тялото на функцията, където неговия час и минути се променят - добавят се към тях тези на обекта right (в главната функция в първото присвояване right става равен на at2). И трето, функцията връща стойност, която е равна на новата стойност на обекта, който се променя. Това става, чрез извикване на двуаргументния конструктор, който се инициализира с часа и минутите на обекта, който се променя.
return airtime(hours, minutes); // return our new value
Това е много по-добра програмистка практика, вместо в тялото на функцията да създаваме още един локален обект, който после да връщаме като стойност на функцията. Това същото може да се приложи и за аритметичните оператори от предишната лекция. Ето един примерен код на функцията-оператор operator+.
//
overloaded + operator
airtime
operator + (const airtime & right)
{
int thrs = hours + right.hours; // add data
int tmins = minutes + right.minutes;
if(tmins >= 60)
// check for carry
{ thrs++; tmins -= 60; }
return airtime(thrs, tmins); // return
unnamed
}
В курса на Лафоор [1] пише, че това е едно особено твърдение, което не е извикване на конструктор - "This is a very special kind of statement. It looks like a constructor, and you might think it will cause the creation of a new airtime object.". Но за да се изпълни това твърдение, то трябва да имаме в класа двуаргументен конструктор, написан от нас, иначе компилаторът се оплаква и не успява да компилира програмата:
[C++ Error]: E2285 Could not find a match for 'airtime::airtime(int,int)'.
5. Презареждане на други бинарни оператори. В курса на Лафоор [1] е дадено упражнение да се напише функция-оператор за умножаване на обект от класа airtime с реално число. Това е една интересна задача, която не е трудно да бъде решена от читателя, но въпреки това ние ще дадем по-долу едно наше решение.
//
exer2.cpp (Exercise 2)
//
class models time data type
//
overloads the + and - operators
//
overloads also "airtime operator *= (int right)"
#include
<iostream.h>
#include
<conio.h>
// for getch()
class
airtime
{
private:
int hours;
// 0 to 23
int minutes;
// 0 to 59
public:
// no argument constructor
airtime(): hours(0), minutes(0)
{ }
// two argument constructor
airtime(int h, int m): hours(h), minutes(m)
{ }
// output to screen
void display() const
{
cout << hours << ':' << minutes;
}
// input from user
void get()
{
char dummy;
cout << "\nEnter time (format 12:59): ";
cin >> hours >> dummy >> minutes;
}
// overloaded + operator
airtime operator + (airtime right)
{
// make a temporary object
airtime temp;
// add data
temp.hours = hours + right.hours;
temp.minutes = minutes + right.minutes;
// check for carry
if (temp.minutes >= 60)
{
temp.hours++;
temp.minutes -= 60;
}
return temp; // return
temporary object
}
// overloaded - operator
airtime operator - (airtime right)
{
// make a temporary object
int mins, hous;
mins = (hours*60 + minutes) - (right.hours*60 + right.minutes);
hous = mins / 60;
mins -= (hous*60);
return airtime(hous, mins);
// return temporary object
}
// overloaded * operator
airtime operator * (int right)
{
// make a temporary object
int mins, hous;
mins = (hours*60 + minutes)*right;
hous = mins / 60;
mins -= (hous*60);
return airtime(hous, mins);
// return temporary object
}
// overloaded *= operator
airtime operator *= (int right)
{
minutes = (hours*60 + minutes)*right;
hours = minutes / 60;
minutes -= (hours*60);
return airtime(hours, minutes); // return
temporary object
}
};
// end class airtime
void
main()
{
airtime at1, at2, at3;
int mult;
cout << "Enter first airtime: ";
at1.get();
cout << "Enter second airtime: ";
at2.get();
cout << "Enter a multiplier: ";
cin >> mult;
at3 = at1 + at2; // overloaded
+ operator
// adds at2 to at1
cout << "\nsum = ";
at3.display();
// display sum
at3 = at1 - at2; // overloaded
+ operator
// adds at2 to at1
cout << "\ndifference = ";
at3.display();
// display sum
at3 = at1*mult;
cout << "\nmultiplication = ";
at3.display();
// display sum
at1 *= mult;
cout << "\nmultiplication (t1 *= mult) = ";
at3.display();
// display sum
getch();
}
// main()
Ето и изходът от едно примерно изчисление:
Enter
first airtime:
Enter
time (format 12:59): 23:34
Enter
second airtime:
Enter
time (format 12:59): 13:27
Enter
a multiplier: 3
sum
= 37:1
difference
= 10:7
multiplication
= 70:42
multiplication
(t1 *= mult) = 70:42
В следващата
лекция ще се занимаем с презареждането на унарните оператори (Overloading
Unary Operators).
.