Deprecated: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead in /home/studb20/public_html/index.php on line 4
 2.8 Перевантаження операцій - Програмування С, С++теорія та практика (частина 2) - Studbook

Програмування С, С++теорія та практика (частина 2)

2.8 Перевантаження операцій

Ключове слово орегаґог використовується для того, аби визначити нову, перевантажену дію конкретного оператора мови. Як і у випадку з перевантаженими функціями, компілятор відрізнятиме різні функції за контекстом звертання числом і типом параметрів та операндів. Перевантаження операторів не вносить нічого нового у мові, з чим би ми не зустрічалися раніше, якщо лише згадати перевантаження функцій (див. розділ 2.2.5 "Перевантаження функцій"). Це дає можливість використовувати об’єкти у виразах замість того, щоб передавати їх як параметри у функції. Кожному оператору мова Сі++ ставить у відповідність ім’я функції, що складається з ключового слова орегаґог, власне оператору та аргументів відповідних типів:

<тип повертання> орегаЬг<символ оператору> (<вхідні параметри>);

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

Т орега£ог+(Т а, Т а);

Семантика такого запису повністю аналогічна функції Т айй (Т а, Т Ь), яка, можливо, і більш звична для сприйняття, проте спосіб перевантаження оператора має певну вигоду. Як перевантажений оператор, функція орегаґог+() може викликатися у виразах скрізь, де використовується знак + та мають місце представники класу Т:

Т а1, а2;

а1+а2;  // аналогічно  орегагог +  (а1,а2); або

агігі(а1,а2);

Перевантаження операцій підпорядковується наступним правилам:

•         при перевантаженні зберігаються кількість аргументів, пріоритети операцій та правила асоціації, що використовуються у стандартних типах даних;

•         для стандартних типів даних операції не підлягають перевизначенню;

•         перевантажена функція-оператор не може мати параметрів по замовчуванню, не успадковується та не може бути визначеною як ііаііс.

•            функція-оператор може бути визначена трьома способами - метод класу, дружня функція або звичайна функція. В останніх двох випадках вона повинна приймати хоча б один аргумент, що має тип класу, покажчика або посилання на клас.

Як перший приклад, що використовує перевантажені зовнішні Угіеп^-функції, розглянемо перевантаження бінарних операцій "+" та "-":

Приклад 1.

// клас приймає рядок як константу, перетворюючи його в // еквівалент типу Іопд #іпс1ийе <іоз^геат.Ь>

#іпс1ийе <з^гіпд.Ь>

#іпс1ийе <5'Ьгі1іЬ.Ь> сіазз Т {

рг^а^е:

сЬаг Vа1ие[20]; риЬііс:

Т() ^а1ие[0]=0; }

Т(сопз£ сЬаг *з) ;

1опд де^а1ие ^оій.)

{

ге^игп а£о1^а1ие) ;

}

// функції оголошуються як друзі, отже мають // доступ до усіх членів об'єкту Т £гіепй 1опд орега^ог +(Т а, Т Ь);

£гіепй 1опд орега^ог -(Т а, Т Ь);

};

Т::Т(сопз£ сЬаг *з)

{

з^гпсру(Vа1ие,з,11);

Vа1ие[11]=0;

1опд орега£ог+( Т а, Т Ь)

{

ге'Ьигп (а£о1 (а.Vа1ие) +а£о1 (Ь.Vа1ие) ) ;

}

1опд орега'Ьог- ( Т а, Т Ь)

{

ге'Ьигп (а£о1 (а.Vа1ие) -а£о1 (Ь^а1ие) ) ;

}

таіп () {

Т а="23";

// ініціалізація об'єктів символьними рядками Т Ь="22";

сои^<< "Уа1ие о£ а="<< а.де^а1ие(); сои^<< "\пУа1ие о£ Ь="<< Ь.де^а1ие(); сои^<< "\п а+Ь=="<<(а+Ь);

// використання перевантажених операторів сои'Ь<< "\п а-Ь=="<<(а-Ь)<<"\п"; ге'Ьигп 0;

}

Таким чином можливе вільне оперування значеннями за допомогою звичайних операторів (+ та -), не застосовуючи при цьому операції перетворення до рядків, якщо тільки вони будуть вищевказаного типу Т. Перевантажені функції-оператори були зовнішніми, а тому обов'язково визначалися з ключовим словом /гіепсі, аби мати доступ до елементів-даних з протокольної частини класу.

Перевантажені функції-операції можуть бути і членами класу, причому обов'язково нестатичними (згадаємо, що такі функції мають прихований аргумент ґкіз). За рахунок цього і з'являється відмінність в реалізації такої функції перевантаження на відміну від подібної зовнішньої. Саме про це і йтиметься нижче. Розглянемо клас Сигг, що використовується для представлення грошової форми національної валюти:

Приклад 2.

с1азз Сигг{ рго'ЬесЬей:

ипзідпей іп£ дгіупуа; // значення у гривнях ипзідпей іп£ сор; // значення у копійках риЬ1іс:

Сигг(ипзідпей іп£ й, ипзідпей іп£ с)

{

дгіупуа=гі.; сор=с;

мЬі1е(сор>=100){

// приведення до коректного грошового вигляду

дгіупуа++; сор-=100;

}

}

Сигг орегагог +( Сигг& з2);

};

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

// визначення класової функції орегаЬог +

Сигг Сигг::орегаЬог +( Сигг& з2)

{

//оператор + додає "поточний" екземпляр до з2, зберігаючи // результат в новій змінній сор=сор+з2.сор; дгіупуа=дгіупуа+з2 . дгіупуа;

Сигг Vа1 (гі,с); ге'Ьигп Vа1;

}

Для кращого порівняння наведемо паралельний виклад аналогічної зовнішньої Уг/еий-функції, що не є членом класу, який теж міг бути застосованим у даному випадку:

// додає з1 до з2, зберігаючи результат в новій змінній £гіепгі Сигг орегагог +(Сигг& з1, Сигг& з2)

{

іп£ с=з1.сор+з2.сор;

іп£ Й=з1.дгіупуа+з2.дгіупуа;

Сигг Vа1 (Й,с); ге'Ьигп Vа1;

}

Функції практично ідентичні, за винятком хіба що кількості параметрів. Оператор не змінює значення жодного із своїх аргументів, створюючи новий екземпляр та повертаючи його значення. Але якщо у зовнішній версії складаються екземпляри зі та з2, то у випадку функції-елемента класу першим виступатиме "поточний" екземпляр, для якого викликатиметься функція (саме той, на який і посилається покажчик ґкіз), а вже другим йтиме з2. На відміну від реалізації використання таких функцій для обох випадків не відрізнятиметься:

Сигг зит1(5,50);

Сигг зит2(7,55);

Сигг зит(0,0);

8ит=зит1+зит2 ;

Отже, у функції-елементі, що реалізує перевантажений оператор, завжди на один аргумент менше, аніж в аналогічної зовнішньої функції, оскільки лівий аргумент у такому випадку завжди передається неявно. Ця відмінність явно проглядається і у перевантаженні унарних операцій:

•       як дружніх функцій:

£гіепй 1опд орега^ог-(Та)

{

ге^игп -а^о^а^а^е);

}

•         як функцій-елементів:

1опд Т: : орега^ог - ^оігі.)

{

ге^игп -а£о1(Vа1ие);

} // або -а'Ьо1('ЬЬіз-^а1ие);

Зауваження щодо перевантаження операцій.

1.     Існують обмеження на перевантаження: не підлягають цій процедурі селектор елемента структури (.), оператор доступу до елементу за покажчиком (*), операція дозволу видимості (::), символи препроцесору (#, ##) та шео/(). Неможливим є введення власних операторів та зміна їх пріоритетів. Крім того, неможливо перевантажувати оператори для вбудованих типів.

2.     Компілятор С++ не розуміє семантики перевантаженого оператору, а отже, не нав’язує ніяких математичних концепцій. Ніщо вам не завадить перевантажити, скажімо, оператор інкременту в якості зменшення аргументу, проте навряд чи в цьому є хоч трохи здорового глузду. Якщо не слідувати традиційному осмисленню обчислень, результати роботи вашої програми для стороннього спостерігача можуть бути збентежуючими.

3.     Не існує виведення складних операторів з простих: якщо ви перевантажили оператори орегаІог+ та орегаґог=, це зовсім не

означає, що С++ обчислить вираз а+=Ь, оскільки ви не перевантажили орегаґог +=.

4.     Перевантаження бінарних операторів не тотожньо відносно перестановки аргументів місцями, тим більше, якщо вони різного типу. Оскільки, скажімо, орегаІог*(гїоиЬ1е, Т&) та орегаІог*(Т&, гїоиЬІе)          володіють різними параметрами, їх необхідно

перевизначати окремо. Зручно це зробити, не створюючи новий код у другому операторі, а послатися у ньому на визначений перший оператор, змінивши лише порядок слідування аргументів:

 

Т орега'Ьог*(йоиЬ1е £, Т &з) {

//реалізація методу

}

Т орега'Ьог*(йоиЬ1е £, Т &з)

{

ге'Ьигп £*з;

}

 

 

 

38