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-пам'яті.".


 

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

16254. Изучение головной станции кабельного телевидения 2.42 MB
  Лабораторная работа №1 Лабораторная работа №1 Изучение головной станции кабельного телевидения 1 Цель работы: 1.1 Изучить принципы построения головной станции кабельного телевидения. 1.2 Научиться производить настройку головной станции кабельного телевидения. ...
16255. Исследование структуры и принципа действия конвертора телевизионных сигналов 856.5 KB
  Лабораторная работа №2 Исследование структуры и принципа действия конвертора телевизионных сигналов 1 Цель работы: 1.1 Изучить структурную схему конвертора СТ07. 1.2 Научиться производить настройку конвертора СТ07. 2 Литература: 2.1 Джакония В.Е. Телевидение. М.:
16256. Исследование спектра сигнала кабельной сети 991 KB
  Лабораторная работа №4 Исследование спектра сигнала кабельной сети 1 Цель работы: 1.1 Исследовать спектр сигнала Архангельской телевизионной компании. 1.2 Научиться пользоваться анализатором спектра DL4. 1.3 Научиться пользоваться программным обеспечением S.M.A.R.T. ...
16257. Удосконалення бухгалтерської звітності підприємства 473.5 KB
  Предметом дослідження є сукупність теоретичних, методологічних і організаційно економічних питань з удосконалення бухгалтерської звітності в ППА «Коровинці» Недригайлівського району Сумської області
16258. Исследование структуры и принципа действия конвертора телевизионных сигналов 677 KB
  Лабораторная работа №2 Исследование структуры и принципа действия конвертора телевизионных сигналов 1 Цель работы: 1.1 Изучить структурную схему конвертора СТ07. 1.2 Научиться производить настройку конвертора СТ07. 2 Литература: 2.1 Джакония В.Е. Телевидение. М.: ...
16259. Исследование структуры и принципа работы модулятора телевизионного МТ-07 799 KB
  Лабораторная работа №3 4 часа Исследование структуры и принципа работы модулятора телевизионного МТ07 1 Цель работы: 1.1 Изучить структурную схему модулятора МТ07. 1.2 Научиться производить настройку модулятора МТ07. 2 Литература: 2.1 Джакония В.Е. Телевидение. М.:...
16260. Сеть кабельного телевидения 4.66 MB
  Практическая работа №3 Сеть кабельного телевидения 1 Цель работы: 1.1 Приобрести теоретические и практические навыки по профессии электромонтёр 3 разряда станционного ТВ оборудования. 1.2 Изучить принципы формирования телевизионного радиосигнала познакомиться с ...
16261. Аппаратно-студийный блок 6.88 MB
  Практическая работа №2 Аппаратностудийный блок Цель работы Приобретение практических и теоретических навыков по профессии электромонтер 3 разряда станционного ТВ оборудования. Литература 2.1 Колин К.Т. Телевидение – Москва: Радио и...
16262. Телевизионный приемник 3.3 MB
  Практическая работа №4 Телевизионный приемник 1 Цель работы: 1.1 Приобрести теоретические и практические навыки по профессии электромонтёр 3 разряда станционного ТВ оборудования. 1.2 Изучить принципы построения телевизионных приемников. 1.3 Научиться производить...