67264

Перевантаження унарних операторів «++» та «--»

Лекция

Информатика, кибернетика и программирование

Можна перевантажувати унарні оператори інкремента "++" та декремента "--", або унарні "-" і "+". Як уже зазначалося вище, при перевантажені унарного оператора за допомогою функції-члена класу операторній функції жоден об'єкт не передається безпосередньо.

Украинкский

2014-09-06

91.5 KB

11 чел.

Лекція № 11

Тема: Перевантаження унарних операторів "++" та "--"

План

  1.  Перевантаження префіксної форми унарного оператора інкремента "++"
  2.  Перевантаження унарного оператора інкремента "++"  з використанням його префіксної та постфіксної форм

  1.  Перевантаження префіксної форми унарного оператора інкремента "++"

    Можна  перевантажувати  унарні  оператори  інкремента "++"  та  декремента "--", або унарні "-" і "+". Як уже зазначалося вище, при перевантажені унарного оператора за допомогою функції-члена класу операторній функції жоден об'єкт не передається безпосередньо. Операція ж здійснюється над об'єктом, який викликає цю функцію через опосередковано переданий показник  this. Наприклад, розглянемо дещо змінену версію попереднього коду програми. У наведеному нижче його варіанті для об'єктів типу kooClass визначається бінарна операція віднімання та унарна операція інкремента .

Приклад1. Демонстрація механізму перевантаження префіксної форми унарного

                  оператора інкремента "++"

class kooClass

{   

 int x, y, z;        // Тривимірні координати

public:  

kooClass() {x = y = z = 0; }

kooClass(int c, int d, int f) {x = c; y = d; z = f; }

kooClass operator-(kooClass obj);        // Операнд obj передається неявно.

kooClass operator=(kooClass obj);        // Операнд obj передається неявно.

kooClass operator++();        // Префіксна форма оператора інкремента "++"

 void Show(char *s);

};

 

    // Перевантаження бінарного оператора віднімання "-".

kooClass kooClass::operator-(kooClass obj)

{

kooClass tmp;            // Створення тимчасового об'єкта

tmp.x = x - obj.x;          // Операції віднімання цілочисельних значень

tmp.y = y - obj.y;          // зберігають початковий вміст операндів.

tmp.z = z - obj.z;

 return tmp;     // Повертає модифікований тимчасовий об'єкт

}

 

    // Перевантаження оператора присвоєння "=".

kooClass kooClass::operator=(kooClass obj)

{

x = obj.x;        // Операції присвоєння цілочисельних значень

y = obj.y;        // зберігають початковий вміст операндів.

z = obj.z;

 return *this;     // Повернення модифікованого об'єкта операнда,

                          адресованого показником

}

    // Перевантаження префіксної форми унарного оператора інкремента "++".

kooClass kooClass::operator++()

{

x++;      // Інкремент координат x, y і z

y++;

z++;

 return *this;     // Повернення модифікованого об'єкта операнда,

                          адресованого показником

}

 

   // Відображення тривимірних координат x, y, z.

void kooClass::Show(char *s)

{

cout << "Koordunatu obj  <" << s << ">: ";

cout << "\t\tx= " << x << ", y= " << y << ", z= " << z << endl;

}

 

void main()

{

kooClass ObjA(1, 2, 3), ObjB(10, 10, 10), ObjC;

ObjA.Show("A");

ObjB.Show("B");

ObjC = ObjA - ObjB;         // Віднімання об'єктів ObjA і ObjB

ObjC.Show("C = A-B");

ObjC = ObjA - ObjB - ObjC;        // Множинне віднімання об'єктів

ObjC.Show("C = A-B-C");

ObjC = ObjB = ObjA;          // Множинне присвоєння об'єктів

ObjB.Show("B=A");

ObjC.Show("C=B");

++ObjC;          // Префіксний інкремент об'єкта ObjC

ObjC.Show("++C");

ObjA = ++ObjC;          // Префіксний інкремент об'єкта ObjC

ObjC.Show("C");

ObjA.Show("A = ++C");

} 

   

   Внаслідок виконання ця програма відображає на екрані такі результати:

Координати об'єкта <A>:    x= 1, y= 2, z= 3

Координати об'єкта <B>:    x= 10, y= 10, z= 10

Координати об'єкта <C=A-B>:    x= -9, y= -8, z= -7

Координати об'єкта <C=A-B-C>:  x= 0, y= 0, z= 0

Координати об'єкта <B=A>:    x= 1, y= 2, z= 3

Координати об'єкта <C=B>:    x= 1, y= 2, z= 3

Координати об'єкта <++С>:    x= 2, y= 3, z= 4

Координати об'єкта <С>:    x= 3, y= 4, z= 5

Координати об'єкта <A=++С>    x= 3, y= 4, z= 5

     Як  видно  з  останнього  рядка  результату  виконання  програми,  операторна функція operator++() інкрементує кожну координату об'єкта і повертає модифіковане його значення, яке повністю узгоджується з традиційною дією оператора інкремента "++".

  1.  Перевантаження унарного оператора інкремента "++"  з використанням його префіксної та постфіксної форм

     Як уже зазначалося вище, оператори  інкремента "++" та декремента "--" мають дві форми – префіксну  і постфіксну. Наприклад, оператор  інкремента можна використовувати у префіксній формі

++ObjC;

і у постфіксній формі

ObjC++;.

      Як зазначено в коментарях до попереднього коду програми, операторна функція operator++() визначає префіксну форму операції  інкремента "++" для класу kooClass. Але  це  не  заважає  перевантажувати  і  його  постфіксну  форму. Оголошення прототипу постфіксної форми унарного оператора інкремента "++" для класу kooClass має такий вигляд:

kooClass kooClass::operator++(int notused);

Параметр notused не використовується самою функцією. Він слугує індикатором  для  компілятора,  який  дає  змогу  відрізнити  префіксну форму  оператора  інкремента  від постфіксної. Нижче наведено один  з можливих  способів реалізації постфіксної форми унарного оператора інкремента "++" для класу kooClass:

     

       // Перевантаження постфіксної форми унарного оператора інкремента "++".

kooClass kooClass::operator++(int notused)

{

kooClass tmp = *this;          // Збереження початкового значення об'єкта

x++;                     // Інкремент координат x, y і z

y++;

z++;

return tmp;           // Повернення початкового значення об'єкта

}

    Зверніть увагу на те, що ця операторна функція зберігає початкове значення операнда шляхом виконання такої настанови:

kooClass tmp = *this;

Збережене значення операнда (у об'єкті  tmp) повертається за допомогою настанови  return. Потрібно мати на увазі, що  традиційний постфіксний оператор  інкремента  спочатку  набуває  значення  операнда,  а  потім  його  інкрементує. Отже, перш ніж  інкрементувати поточне  значення  операнда, його потрібно  зберегти,  а потім повернути (не забувайте, що постфіксний оператор  інкремента не повинен повертати модифіковане значення свого операнда). У наведеному нижче коді програмі реалізовано обидві форми унарного оператора інкремента "++".

Приклад 2. Демонстрація механізму перевантаження унарного оператора інкремента

                  "++"  з використанням його префіксної та постфіксної форм

class kooClass

{   

 int x, y, z;        // Тривимірні координати

public:

kooClass() { x = y = z = 0; }

kooClass(int c, int d, int f) {x = c; y = d; z = f; }

kooClass operator*(kooClass obj);      // Операнд obj передається неявно.

kooClass operator=(kooClass obj);      // Операнд obj передається неявно.

kooClass operator++();          // Префіксна форма оператора інкремента "++"

kooClass operator++(int notused);       // Постфіксна форма оператора

                                                інкремента "++"

kooClass operator-();        // Префіксна форма унарного оператора зміни

                                     знаку "-"

 void Show(char *s);

};

 

    // Перевантаження бінарного оператора множення "*".

kooClass kooClass::operator*(kooClass obj)

{

kooClass tmp;        // Створення тимчасового об'єкта  

tmp.x = x * obj.x;        // Операції множення цілочисельних значень

tmp.y = y * obj.y;        // зберігають початковий вміст операндів

tmp.z = z * obj.z;

 

 return tmp;       // Повертає модифікований тимчасовий об'єкт

}

 

   // Перевантаження оператора присвоєння "=".

kooClass kooClass::operator=(kooClass obj)

{

x = obj.x;     // Операції присвоєння цілочисельних значень

y = obj.y;     // зберігають початковий вміст операндів

z = obj.z;

 

 return *this;      // Повернення модифікованого об'єкта операнда,

                           адресованого показником

}

 

    // Перевантаження префіксної форми унарного оператора інкремента "++".

kooClass kooClass::operator++()

{

x++;     // Інкремент координат x, y і z

y++;

z++;

 

 return *this;     // Повернення модифікованого об'єкта операнда,

                          адресованого показником

}

 

    // Перевантаження постфіксної форми унарного оператора інкремента "++".

kooClass kooClass::operator++(int notused)

{

kooClass tmp = *this;     // Збереження початкового значення об'єкта

x++;       // Інкремент координат x, y і z

y++;

z++;

 return tmp;      // Повернення початкового значення об'єкта

}

 

   // Перевантаження префіксної форми унарного оператора зміни знаку "-".

kooClass kooClass::operator-()

{

x=-x;      // Зміна знаку координат x, y і z

y=-y;

z=-z;

 return *this;        // Повернення модифікованого об'єкта операнда,                          

                             адресованого показником

}

 

    // Відображення тривимірних координат x, y, z.

void kooClass::Show(char *s)

{

cout << "Координати об'єкта <" << s << ">: ";

cout << "\t\tx= " << x << ", y= " << y << ", z= " << z << endl;

}

 

void main()

{

kooClass ObjA(1, 2, 3), ObjB(10, 10, 10), ObjC;

ObjA.Show("A");

ObjB.Show("B");  

ObjC = ObjA * ObjB;           // Множення об'єктів ObjA і ObjB

ObjC.Show("C=A*B");

ObjC = ObjA * ObjB * ObjC;     // Множинне множення об'єктів

ObjC.Show("C=A*B*C");

ObjC = ObjB = ObjA;            // Множинне присвоєння об'єктів

ObjC.Show("C=B");

ObjB.Show("B=A");

++ObjC;                // Префіксна форма операції інкремента

ObjC.Show("++C");

ObjC++;                // Постфіксна форма операції інкремента

ObjC.Show("C++");

ObjA = ++ObjC;         // Об'єкт ObjA набуває значення об'єкта ObjC після

                               його інкрементування.

ObjA.Show("A = ++C");        // Тепер об'єкти ObjA і ObjC мають однакові

                                     значення.

ObjC.Show("C");

ObjA = ObjC++;         // Об'єкт ObjA набуває значення об'єкта ObjC до його

                               інкрементування.

ObjA.Show("A=C++");    // Тепер об'єкти ObjA і ObjC мають різні значення.

ObjC.Show("C");

-ObjC;                 // Префіксна форма операції зміни знаку

ObjC.Show("-C");

}

   Внаслідок виконання ця програма відображає на екрані такі результати:

Координати об'єкта <A>:    x= 1, y= 2, z= 3

Координати об'єкта <B>:    x= 10, y= 10, z= 10

Координати об'єкта <C=A*B>:    x= 10, y= 20, z= 30

Координати об'єкта <C=A*B*C>:  x= 100, y= 400, z= 900

Координати об'єкта <C=B>:    x= 1, y= 2, z= 3

Координати об'єкта <B=A>:    x= 1, y= 2, z= 3

Координати об'єкта <++С>:    x= 2, y= 3, z= 4

Координати об'єкта <С++>:    x= 3, y= 4, z= 5

Координати об'єкта <A=++С>:    x= 4, y= 5, z= 6

Координати об'єкта <С>:    x= 4, y= 5, z= 6

Координати об'єкта <A=C++>:    x= 4, y= 5, z= 6

Координати об'єкта <С>:    x= 5, y= 6, z= 7

Координати об'єкта <-С>:    x= -5, y= -6, z= -7

     Як  підтверджують  останні  рядки  результату  виконання  програми,  при  префіксному  інкрементуванні об'єкта ObjC його значення збільшується до виконання операції присвоєння об'єкту ObjA, при постфіксному інкрементуванні – після виконання операції присвоєння.

В залежності від положення операндів щодо знака операції розрізняють префіксні (напр., sin x (x - операнд)), інфіксні (наприклад, a + b (a, b - операнди)) і постфіксні (наприклад, x3 (x - операнд)) записи операції.

Тема: Перевантаження операторів відношення та логічних операторів 

   

    Оператори відношення (наприклад, "==", "<", ">", "<=", ">=", "!=")  і логічні

оператори (наприклад, "&&" або "") також можна перевантажувати, причому механізм їх реалізації не представляє жодних труднощів. Як правило, перевантажена операторна функція відношення повертає об'єкт того класу, для якого вона перевантажується.  А  будь-який  перевантажений  оператор  відношення  або  логічний оператор повертає одне з двох можливих значень: true або false. Це відповідає звичайному застосуванню цих операторів  і дає змогу використовувати  їх в умовних виразах.

     Розглянемо приклад перевантаження операторної функції дорівнює "==" для

вже розглянутого вище класу kooClass:

     // Перевантаження операторної функції дорівнює "=="

bool kooClass::operator==(kooClass obj)

{

if((x == obj.x) && (y == obj.y) && (z == obj.z))

return true;

else

return false;

}

    Якщо вважати, що операторна функція operator==() вже реалізована, то такий

код програми є абсолютно коректним:

kooClass ObjA, ObjB;

//...

if(ObjA == ObjB) cout << "ObjA = ObjB" << endl;

else cout << "ObjA не дорівнює ObjB" << endl;

    Оскільки  операторна функція  operator==()  повертає  результат  типу  bool,  то  її можна використовувати для керування настановою if.

Тема: Особливості реалізації оператора присвоєння

    На попередніх лекціях ми розглядали потенційну проблему, пов'язану з передачею об'єктів функціям і поверненням їх з них. У обох випадках проблема виникла під час використання конструктора за замовчуванням, який створює побітову копію об'єкта. Згадаймо, раніше це питання ми вирішували шляхом створення власного конструктора копії, який точно визначає, як повинна бути створена копія об'єкта.

    Подібна проблема може виникати  і під час присвоєння одного об'єкта  іншому.  За  замовчуванням  об'єкт,  який  знаходиться  з  лівого  боку  від  операції  присвоєння "=", отримує побітову копію об'єкта, який знаходиться справа. До негативних  наслідків  це може  призвести  у  випадках,  коли  при  створенні  об'єкт  виділяє певний ресурс (наприклад, пам'ять), а потім змінює його або звільняє. Якщо після виконання операції присвоєння об'єкт змінює або звільняє цей ресурс, то другий об'єкт також змінюється, оскільки він все ще використовує його ресурс. Вирішувати це питання можна також шляхом перевантаження оператора присвоєння.

    Щоб до кінця зрозуміти суть описаної вище проблеми, розглянемо таку (некоректну) програму.

Приклад1. Демонстрація механізму появи помилки, яка виникає при поверненні об'єкта з

                  функції

class strClass

{  

 char *s;

public:

strClass() { s = 0; }

strClass(const strClass &obj);  // Оголошення конструктора копії

~strClass() {if(s) delete[]s; cout << "Zvilnennja s-pamjat" << endl; }

 void Show(char *c) { cout << c << s << endl; }

 void Set(char *str);

};

 

   // Визначення конструктора копії.

strClass::strClass(const strClass &obj)

{

s = new char[strlen(obj.s)+1];

strcpy(s, obj.s);

}

  

   // Завантаження рядка.

void strClass::Set(char *str)

{

s = new char[strlen(str)+1];

strcpy(s, str);

}

 

   // Ця функція повертає об'єкт типу strClass.

strClass Init()

{

 char str[80];

strClass obj;

cout << "Vvedit rjadok: "; cin >> str;

obj.Set(str);

 return obj;

}

 

void main()

{

strClass Obj;    // Створення об'єкта класу

        // Присвоюємо об'єкт, повернутий функцією Init(), об'єкту Obj.

Obj = Init();    // Ця настанова генерує помилку!!!!

Obj.Show("s= ");

}

   Можливі результати виконання цієї програми мають такий вигляд:

Введіть рядок: Привіт

Звільнення s-пам'яті.

Звільнення s-пам'яті.

s= тут "сміття"

Звільнення s-пам'яті.

    Залежно від використовуваного компілятора, на  екрані монітора Ви можете побачити переважно "сміття" або й ні. Програма може також згенерувати помилку тривалості її виконання. У будь-якому випадку помилки не минути. І ось чому.

    У цьому коді програми конструктор копії коректно обробляє повернення об'єкта функцією  Init(). Згадаймо, у разі, коли функція повертає об'єкт, то для  зберігання  повернутого  нею  значення  створюється  тимчасовий  об'єкт.  Оскільки  при створенні об'єкта-копії конструктор копії виділяє нову область пам'яті, то член-даних s початкового об'єкта  і член-даних s об'єкта-копії вказуватимуть на різні області пам'яті, які, як наслідок, не стануть псувати один одного. Проте помилки не минути, якщо повернутий функцією об'єкт присвоюється об'єкту Obj, оскільки у процесі виконання операції присвоєння  за  замовчуванням створюється побітова його копія. У цьому випадку тимчасовий об'єкт, який повертається функцією Init(), копіюється в об'єкт Obj. Як наслідок, член obj.s вказує на ту ж саму область пам'яті, що і член s тимчасового об'єкта. Але після виконання операції  присвоєння  в  процесі  руйнування  тимчасового  об'єкта  ця  пам'ять  звільняється. Отже, член obj.s тепер вказуватиме на вже звільнену пам'ять! Окрім цього, пам'ять, яка адресується членом obj.s, повинна бути  звільнена  і після  завершення роботи коду програми, тобто удруге. Щоб запобігти цьому, необхідно перевантажити оператор присвоєння так, щоб об'єкт, який розташовується зліва від оператора присвоєння, виділяв власну область пам'яті.

    Реалізацію цього рішення покажемо у такій відкоректованій програмі.

Приклад2. Демонстрація механізму появи помилки, яка може виникнути при поверненні  

                  об'єкта з функції

using namespace std;

class strClass

{  

 char *s;

public:

strClass();     

strClass(const strClass &obj);    // Оголошення конструктора копії

~strClass() { if(s) delete[]s; cout << "Zvilnennja s-pamjat" << endl; }

 void Show(char *c) { cout << c << s << endl; }

 void Set(char *str);

     

    // Перевантажений оператор присвоєння

strClass operator=(const strClass &obj);

};

 

   // Визначення звичайного конструктора.

strClass::strClass()

{

s = new char ('\0'); // Член s вказує на NULL-рядок.

}

 

   // Визначення конструктора копії.

strClass::strClass(const strClass &obj)

{

s = new char[strlen(obj.s)+1];

strcpy(s, obj.s);

}

 

   // Завантаження рядка.

void strClass::Set(char *str)

{

s = new char[strlen(str)+1];

strcpy(s, str);

}

   // Перевантаження оператора присвоєння "=".

strClass strClass::operator=(const strClass &obj)

{

       /* Якщо виділена область пам'яті має недостатній

         розмір, виділяється нова область пам'яті. */ 

 if(strlen(obj.s) > strlen(s))

{

 delete[]s;

 s = new char[strlen(obj.s)+1];

}

strcpy(s, obj.s);

 return *this;  // Повернення модифікованого об'єкта операнда,                    

                      адресованого показником

}

 

   // Ця функція повертає об'єкт типу strClass.

strClass Init()

{

strClass obj; char str[80];

cout << "Vvedit rjadok: "; cin >> str;

obj.Set(str);

 return obj;

}

 

void main()

{

strClass Obj;  // Створення об'єкта класу

     // Присвоюємо об'єкт, повернутий функцією Init(), об'єкту Obj

Obj = Init();  // Тепер тут все гаразд!

Obj.Show("s= ");

}

    Ця програма тепер відображає такі результати (у припущенні, що на пропози-

цію "Введіть рядок: " Ви введете "Привіт").

Введіть рядок: Привіт

Звільнення s-пам'яті.

Звільнення s-пам'яті.

Звільнення s-пам'яті.

Привіт

Звільнення s-пам'яті.

   Як бачимо, ця програма тепер працює коректно. Спробуйте детально проаналізувати програму і зрозуміти, чому виводиться кожне з повідомлень "Звільнення s-пам'яті.".


 

А также другие работы, которые могут Вас заинтересовать

43323. Страви з круп 152 KB
  Страви з крупів – найкалорійніші вони містять вітаміни В і РР. Для приготування страв і гарнірів із крупів використовують казани на плитні каструлі різної місткості котломір грохот друшляк сита лопатки кухарські виделки шумівки черпаки та інший посуд і інвентар. Перед варінням каші крупу просіюють перебивають і промивають.
43324. АНАЛІЗ КРЕДИТОСПРОМОЖНОСТІ ПІДПРИЄМСТВА НА ПРИКЛАДІ ВАТ «НІКОПОЛЬСЬКИЙ ЗАВОД ФЕРОСПЛАВІВ» 464.5 KB
  Поняття та суть кредитоспроможності підприємства. АНАЛІЗ КРЕДИТОСПРОМОЖНОСТІ ПІДПРИЄМСТВА НА ПРИКЛАДІ ВАТ НІКОПОЛЬСЬКИЙ ЗАВОД ФЕРОСПЛАВІВ. Аналіз кредитоспроможності підприємства та дослідження шляхів покращення його кредитоспроможності. Діяльність підприємства в системі ринкової економіки неможлива без періодичного використання різноманітних форм залучення кредитів.
43325. Розрахунок фінансового стану підприємства 638 KB
  Акціонерне товариство провело деномінацію акцій шляхом дроблення у співвідношенні 1:6 витрати по операції склали 750 грн. Показник Статутний капітал грн. Номінальна вартість грн. Відбудеться зменшення нерозподіленого прибутку на суму 750 грн.
43326. Дослідження та аналіз прямих податків в Україні 586 KB
  Світовий досвід у сфері прямого оподаткування Необхідність вдосконалення системи прямого оподаткування Вступ податок прямий фінансовий Являючи собою неперевершений інструмент вилучення частини приватних доходів на користь суспільних союзів податки вважаються однією з основних рис сучасної цивілізації. Для досягнення поставленої мети вирішувалися такі задачі: дослідження історичних передумов виникнення і розвитку податків; дослідження теоретичних основ...
43327. Розробка програми виведення системного і реального часу на платі IBM PC 1.19 MB
  Розробка програми виведення системного і реального часу на платі IBM PC полягає в тому що розробка програми має велике значення як для навчального процесу так і для створення складних систем оскільки будьяка потужна система базується саме на більш простих системах і розуміння цих процесів значно полегшує роботу зі складними системами дії яких приховані від користувача. Крім цього на сьогодні існує проблема що стосується корекції системного часу а точніше багато комп’ютерів які беруть участь у процесі виробництва і керують ним...
43328. Використання електронних підручників на уроках хімії 143.5 KB
  Використання комп'ютерних моделей дозволяє розкрити істотні зв'язки досліджуваного об'єкта, глибше виявити його закономірності, що, у кінцевому рахунку, веде до кращого засвоєння матеріалу. Учень може досліджувати явище, змінюючи параметри, порівнювати отримані результати, аналізувати їх, робити висновки. Наприклад, задаючи різні значення концентрації реагуючих речовин (у програмі, що моделює залежність швидкості хімічної реакції від різних факторів), учень може простежити за зміною обсягу газу, що виділяється, і т.д.
43329. Оборотні кошти підприємства 516 KB
  Висока інфляція неплатежі й інші кризові явища змушують підприємства змінювати свою політику стосовно оборотних коштів шукати нові джерела поповнення вивчати проблему ефективності їхнього використання. Тема визначення потреби в оборотних коштах є досить актуальною бо правильна організація збереження і ефективність використання оборотних коштів мають велике значення для забезпечення безперервного процесу суспільного відтворення стійкого фінансового стану всіх суб'єктів господарювання нормального грошового звернення реального накопичення...
43330. Вузол черв’ячного редуктора 5.02 MB
  Пустотілий вал 1 черв’ячного колеса розміщений у корпусі редуктора на конічних роликових підшипниках 0-го класу точності. На вал 1 діє нерухома радіальна сила – 8 кН. Вінець черв’ячного колеса не розбирається і повинен передавати Мкр=2000 кН мм на маточину. В деталі 2 є шліцевий отвір, в який заходить шліцевий вал, непоказаний на кресленні. Даний вал може вільно переміщатись в осьовому напрямку.