Най-често се презареждат
от програмистите операторите за нарастване и намаляване, както в тяхната
префиксна форма, така и в постфиксната форма - за тези оператори вижте
т. 3 на лекция 5.
.
1. Функцията-оператор
за префиксната версия на оператора за нарастване (Prefix Version of Operator
++).
Повечето унарни оператори оперират върху аргумент, който се намира в дясно
от тях, но както знаем, този оператор (както и този за намаляване, --)
има две форми. Първо ще разгледаме префиксната му форма, за която
функцията за презареждане на оператора ++ е следната:
SomeClass operator ++ ()
{
// here are the statements for the increment
// of member data of SomeClass
return SomeClass(...); // return object
of SomeClass
}
В случая е дадена общата форма за произволен клас SomeClass. Последният ред във функцията на класа извиква съответен конструктор (едно аргументен или двуаргументен и пр.), който връща обект от класа SomeClass. Обърнете внимание на последното твърдение - това позволява операторът да се използва във вида:
SomCalssObj2 = ++SomCalssObj1;
Обърнете внимание, че функцията-оператор operator++() няма аргументи, работи с обекта, за който се извиква и връща стойност, която е от типа на класа.
В курса на Лафоор [1] е предложена следната версия на оператора:
// overloaded prefix ++ operator
airtime operator++ ()
{
++minutes; // increment
this object
if (minutes >= 60)
{
++hours;
minutes -= 60;
}
// return new value
return airtime(hours, minutes);
}
По същия начин може да се напише и функцията-оператор за префиксната версия на оператора за намаляване --:
// overloaded prefix -- operator
airtime operator-- ()
{
--minutes; // decrement
this object
if (minutes < 0)
{
--hours;
minutes = 59;
}
// return new value
return airtime(hours, minutes);
}
Нека разгледаме следната програма prefix.cpp от курса на Лафоор [1], в която е презареден префиксният оператор за нарастване за класа airtime, и която е изменена от нас с добавянето на префиксен оператор за намаляване --.
//
prefix.cpp
//
overloads the ++ operator, prefix version
#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 prefix ++ operator
airtime operator++ ()
{
++minutes; // increment
this object
if (minutes >= 60)
{
++hours;
minutes -= 60;
}
// return new value
return airtime(hours, minutes);
}
// overloaded prefix -- operator
airtime operator-- ()
{
--minutes; // decrement
this object
if (minutes < 0)
{
--hours;
minutes = 59;
}
// return new value
return airtime(hours, minutes);
}
};
// end class airtime
void
main()
{
airtime at1, at2; // make
two airtimes
at1.get();
// get value for one
++at1; // increment it
cout << "\nat1=";
at1.display();
// display result
// at1++; // error: postfix
at2 = ++at1; // increment again, and assign
cout << "\nat2=";
at2.display();
// display assigned value
--at1; // increment it
cout << "\nat1=";
at1.display();
// display result
// at1--; // error: postfix -- operator
at2 = --at1; // increment again, and assign
cout << "\nat2=";
at2.display();
// display assigned value
getch();
}
// main()
В програмата операторът ++ първо се използва във вида ++at1;, което извършва нарастване на минутите на at1 с единица. Ако погледнете функцията оператор operator++() ще видите, че когато минутите станат равни на 60, те се нулират и часът се увеличава. Разбира се, вие може да презаредите оператора за нарастване така, както вие искате - например само часът да се увеличава с единица, което евентуално би отговаряло на смяна на часовите зони.
Второто извикване на този оператор в главната функция на програмата е от вида at2 = ++at1;. Именно заради този начин на извикване, функцията-оператор трябва да връща стойност, която е от типа на класа. Обърнете внимание, че функцията-оператор е направена така, че първо се променя обектът, а после се връща неговата стойност! Точно това е и замисълът на префиксната форма.
След това, аналогично на първите две извиквания, се извиква два пъти и операторът за намаляване. Ето екранът, който програмата дава, когато потребителят вкара час 8:59.
Enter time (format 12:59): 8:59
at1=9:00
at2=9:01
at1=9:00
at2=8:59
Обърнете внимание на двата реда в програмата
//
at1++; // error: postfix ++ operator
//
at1--; // error: postfix -- operator
които са в коментари, защото е невъзможно да извикате постфиксната версия на оператора, която още не е презаредена.
2. Функцията-оператор за постфиксната версия на оператора за нарастване (Postfix Version of Operator ++). В тази част ще разгледаме примерна програма за постфиксната версия на оператора за нарастване, както и този за намаляване. Създателите на C++ са решили да различават префиксните и постфиксните оператори само чрез добавянето на фиктивен аргумент (dummy argument) в прототипа на функцията-оператор при постфиксния оператор. Този аргумент е от целочислен тип и въобще не участва в никакви твърдения, свързани с действието на оператора. Например за класа airtime разликата в прототипите на префиксния и постфиксния оператори за нарастване е следната:
airtime
operator++ () // prefix version
airtime
operator++ (int) // postfix version
Разбира се, постфиксният оператор при следното извикване
at2 = at1++; // increment again, and assign
действа по следния начин: първо предава предишната стойността на обекта at1 на обекта at2, а чак след това увеличава минутите в at1. Запазването на предишната стойност се осъществява чрез временен обект temp, който се инициализира с двуаргументен конструктор с часа и минутите на обекта, който променяме.
// overloaded postfix ++ operator
airtime operator++ (int)
{
airtime temp(hours, minutes); // save original value
++minutes;
// increment this object
if (minutes >= 60)
{
++hours;
minutes -= 60;
}
// return old original value
return temp;
}
Нека разгледаме следната програма postfix.cpp от курса на Лафоор [1], в която е презареден постфиксеният оператор ++ за класа airtime, и която е изменена от нас с добавянето на постфиксен оператор за намаляване --.
//
postfix.cpp
//
overloads the ++ and -- operators, postfix version
#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 postfix -- operator
airtime operator-- (int)
{
airtime temp(hours, minutes); // save original value
--minutes;
// decrement this object
if (minutes < 0)
{
--hours;
minutes = 59;
}
// return old original value
return temp;
}
// overloaded postfix ++ operator
airtime operator++ (int)
{
airtime temp(hours, minutes); // save original value
++minutes;
// increment this object
if (minutes >= 60)
{
++hours;
minutes -= 60;
}
// return old original value
return temp;
}
};
// end class airtime
////////////////////////////////////////////////////////////////
void
main()
{
airtime at1, at2; // make
two airtimes
cout << "For at1: ";
at1.get();
// get value for one
cout << "\n\n-----\nat1++;" << endl;
at1++;
// increment it
cout << "\nat1=";
at1.display();
// display result
// ++at1; // error: prefix ++ operator
cout << "\n\n-----\nat2 = at1++;" << endl;
at2 = at1++;
// increment again, and assign
cout << "\nat1=";
at1.display();
// display result
cout << "\nat2=";
at2.display();
// display assigned value
cout << "\n\n-----\nat1--" << endl;
at1--;
// decrement it
cout << "\nat1=";
at1.display();
// display result
// --at1; // error: prefix -- operator
cout << "\n\n-----\nat2 = at1--" << endl;
at2 = at1--;
// decrement again, and assign
cout << "\nat1=";
at1.display();
// display result
cout << "\nat2=";
at2.display();
// display assigned value
getch();
}
// main()
Разликата с предишната програма е не само в двата нови оператора, но и че в главната функция са добавени повече твърдения, за да стане ясно как се извършват операциите с тези два оператора. Ето екранът, който програмата дава, когато потребителят вкара час 8:59.
For
at1:
Enter
time (format 12:59): 8:59
-----
at1++;
at1=9:00
-----
at2
= at1++;
at1=9:01
at2=9:00
-----
at1--
at1=9:00
-----
at2
= at1--
at1=8:59
at2=9:00
Обърнете внимание на двата реда в главната функция на програмата:
//
++at1; // error:
prefix ++ operator
//
--at1; // error:
prefix -- operator
които са в коментари, защото е невързможно да извикате префиксната версия на оператора, която не е презаредена (няма написани функции-оператори за тези действия). Като упражнение, читателят може да комбинира двете програми и да напише една, в която и четирите оператора са презаредени.
3. Презареждане на унарния оператор отрицателен знак (The Unary Minus Operator). Даже и да сме презаредили оператора разлика, и например, за комплексните числа поддържани от наша програма да можем да роботим по следния начин:
cn3 = cn2 - cn1;
то е уместно да се презареди и операторът отрицателен знак, защото израз от вида
cn3 = -cn1 + cn2;
няма да може да бъде изчисляван.
Също така следващото присвояване не може да бъде извършено без наличието на презареден оператор отрицателен знак:
cn3 = -cn1;
От последния израз е ясно, че искаме операторът отрицателен знак да връща комплексно число, чийто реална и имагинерна част са противоположни на оригиналните, но самото число да не се променя. За комплексните числа, поддържани от нашата програма в една от предишните лекции, функцията-оператор би изглеждала по този начин:
// Overloaded Unary Minus Operator
complex_number operator-()
{
return complex_number(-real, -imaginary);
}
Обърнете внимание, че прототипът на тази функция се различава от този на функцията-оператор за разлика, в това че няма аргумент.
За да работи, обаче, тази функция е необходимо да имаме написан от нас двуаргументен конструктор
// two-argument constructor
complex_number(float re, float im): real(re), imaginary(im)
{ }
А напишем ли двуаргументен конструктор, ни е необходим и едноаргументен,
// no-argument constructor
complex_number(): real(0.0), imaginary(0.0)
{ }
защото няма да можем да създаваме временните обекти от класа complex_number в другите оператори-функции със следното твърдение:
complex_number cn;
И така, цялата програма complex4.cpp, която е допълнената програма complex3.cpp от една от предишните лекции, е дадена по-долу
//
complex4.cpp
//
a class that models a complex number data type
//
Overloaded Unary Minus Operator
#include
<iostream.h>
#include
<iomanip.h>
class
complex_number
{
private:
float real; // real
part
float imaginary; // imaginery part
public:
void set()
{
char dummy; // for comma
cout << "Enter number (format Re, Im): ";
cin >> real >> dummy >> imaginary;
}
void display()
{
cout << '('
<< showpoint
<< setprecision(6) << real
// real part
<< ','
<< setprecision(6) << imaginary // imaginary part
<< ')';
}
// no-argument constructor
complex_number(): real(0.0), imaginary(0.0)
{ }
// two-argument constructor
complex_number(float re, float im): real(re), imaginary(im)
{ }
// add to the complex number another one (compl_num)
complex_number operator + (complex_number compl_num)
{
complex_number cn;
cn.real = real + compl_num.real;
cn.imaginary = imaginary + compl_num.imaginary;
return cn;
}
// subtract from the complex number another one (compl_num)
complex_number operator - (complex_number compl_num)
{
complex_number cn;
cn.real = real - compl_num.real;
cn.imaginary = imaginary - compl_num.imaginary;
return cn;
}
// multiply two complex numbers and assign the result to third one
complex_number operator * (complex_number compl_num)
{
complex_number cn;
cn.real = real*compl_num.real - imaginary*compl_num.imaginary;
cn.imaginary = real*compl_num.imaginary + compl_num.real*imaginary;
return (cn);
}
// divide the complex number by another one (compl_num)
complex_number operator / (complex_number compl_num)
{
complex_number cn;
cn.real = real;
cn.imaginary = imaginary;
float sqr_mod = compl_num.module();
if (sqr_mod == 0)
{
cout << "Devision by zero!" << endl;
return cn;
}
sqr_mod *= sqr_mod;
cn.real = (real*compl_num.real + compl_num.imaginary*imaginary)/sqr_mod;
cn.imaginary = (compl_num.real*imaginary - real*compl_num.imaginary)/sqr_mod;
return cn;
}
// calculate the module of the complex number
float module()
{
float m = real*real + imaginary*imaginary;
return sqrt(m);
}
// Overloaded Unary Minus Operator
complex_number operator-()
{
return complex_number(-real, -imaginary);
}
}; // class complex_number
void
main()
{
// create three complex_number variables
complex_number c1, c2, c3;
char choice;
do
{
// enter c1
cout << "For c1, ";
c1.set(); // set c1
// enter c1
cout << "For c2, ";
c2.set(); // set c2
cout << endl;
// perform addition and ...
c3 = c1 + c2;
// ... display the result
c1.display();
cout << " + ";
c2.display();
cout << " = ";
c3.display();
cout << endl << endl;
// perform subtraction and ...
c3 = c1 - c2;
// ... display the result
c1.display();
cout << " - ";
c2.display();
cout << " = ";
c3.display();
cout << endl << endl;
// perform multiplication and ...
c3 = c1 * c2;
// ... display the result
c1.display();
cout << " * ";
c2.display();
cout << " = ";
c3.display();
cout << endl << endl;
// perform division and ...
c3 = c1 / c2;
// ... display the result
c1.display();
cout << " / ";
c2.display();
cout << " = ";
c3.display();
cout << endl << endl;
// perform negation and ...
c3 = -c1;
// ... display the result
cout << "- ";
c1.display();
cout << " = ";
c3.display();
cout << endl << endl;
// perform c3 = -c1 + c2 and ...
c3 = -c1 + c2;
// ... display the result
cout << "- ";
c1.display();
cout << " + ";
c2.display();
cout << " = ";
c3.display();
cout << endl << endl;
cout << "\nDo another (y/n)? ";
cin >> choice;
} while(choice != 'n');
} //end main
Ето и изходът от едно примерно изчисление:
For
c1, Enter number (format Re, Im): -1, 3
For
c2, Enter number (format Re, Im): 4, -2
(-1.00000,3.00000) + (4.00000,-2.00000) = (3.00000,1.00000)
(-1.00000,3.00000) - (4.00000,-2.00000) = (-5.00000,5.00000)
(-1.00000,3.00000) * (4.00000,-2.00000) = (2.00000,14.0000)
(-1.00000,3.00000) / (4.00000,-2.00000) = (-0.500000,0.500000)
- (-1.00000,3.00000) = (1.00000,-3.00000)
- (-1.00000,3.00000) + (4.00000,-2.00000) = (5.00000,-5.00000)
Ако изграждаме свои класове за наши обекти, подобно на този, който поддържа комплексните числа, е хубаво и за пълнота на математичните операции да презаредим и унарния положителен оператор (въпреки, че той не променя комплексното число), за да можем да пишем твърдения от рода на:
cn3
= + cn1 - cn2;
cn3
= + cn2;
За упражнение, читателят може да прибави презаредения оператор complex_number operator+() към програмата по-горе и да го тества с новонаписани редове в главната функция.
В следващата
лекция ще се занимаем с превръщането на обекти от наш клас в предефинираните
типове (Conversion from Objects to Basic Types).
.