Студопедия
Новини освіти і науки:
МАРК РЕГНЕРУС ДОСЛІДЖЕННЯ: Наскільки відрізняються діти, які виросли в одностатевих союзах


РЕЗОЛЮЦІЯ: Громадського обговорення навчальної програми статевого виховання


ЧОМУ ФОНД ОЛЕНИ ПІНЧУК І МОЗ УКРАЇНИ ПРОПАГУЮТЬ "СЕКСУАЛЬНІ УРОКИ"


ЕКЗИСТЕНЦІЙНО-ПСИХОЛОГІЧНІ ОСНОВИ ПОРУШЕННЯ СТАТЕВОЇ ІДЕНТИЧНОСТІ ПІДЛІТКІВ


Батьківський, громадянський рух в Україні закликає МОН зупинити тотальну сексуалізацію дітей і підлітків


Відкрите звернення Міністру освіти й науки України - Гриневич Лілії Михайлівні


Представництво українського жіноцтва в ООН: низький рівень культури спілкування в соціальних мережах


Гендерна антидискримінаційна експертиза може зробити нас моральними рабами


ЛІВИЙ МАРКСИЗМ У НОВИХ ПІДРУЧНИКАХ ДЛЯ ШКОЛЯРІВ


ВІДКРИТА ЗАЯВА на підтримку позиції Ганни Турчинової та права кожної людини на свободу думки, світогляду та вираження поглядів



Лекція 17

Тема : Перевантаження методів та операторів.

При визначенні функцій у своїх програмах ви повинні вказати тип повертається функцією значення, а також кількість параметрів і тип кожного з них. У минулому ( якщо ви програмували на мові С) , коли у вас була функція з ім'ям add_values , яка працювала з двома цілими значеннями , а ви хотіли б використовувати подібну функцію для складання трьох цілих значень , вам слід було створити функцію з іншим ім'ям. Наприклад , ви могли б використовувати add_two_values і add_three_values. Аналогічно якщо ви хотіли використовувати подібну функцію для складання значень типу float , то вам була б необхідна ще одна функція з ще одним ім'ям . Щоб уникнути дублювання функції , C + + дозволяє вам визначати кілька функцій з одним і тим же ім'ям. У процесі компіляції C + + бере до уваги кількість аргументів , що використовуються кожною функцією , і потім викликає саме потрібну опцію. Надання компілятору вибору серед декількох функцій називається перевантаженням . У цьому уроці ви навчитеся використовувати перевантажені функції . До кінця даного уроку ви освоїте такі основні концепції:

• Перевантаження функцій дозволяє вам використовувати одне і те ж ім'я для кількох функцій з різними типами параметрів .

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

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

ПЕРШЕ ЗНАЙОМСТВО З перевантаження функцій

Перевантаження функцій дозволяє вашим програмам визначати кілька функцій з одним і тим же ім'ям і типом значення, що повертається . Наприклад , наступна програма перевантажує функцію з ім'ям add_values ​​. Перше визначення функції складає два значення типу int . Друге визначення функції складає три значення . У процесі компіляції C + + коректно визначає функцію , яку необхідно використовувати:

# include <iostream.h>

int add_values ( int a , int b )

{
return ( a + b ) ;
)

int add_values ( int a , int b , int c )

(
return ( a + b + c ) ;
)

void main ( void )

{
cout << " 200 + 801 = " << add_values ( 200 , 801) << endl ;
cout << " 100 + 201 + 700 = " << add_values (100, 201 , 700 ) << endl ;
}

 

Як бачите , програма визначає дві функції з іменами add_values ​​Перша функція складає два значення типу int , в той час як друга складає три значення . Ви не зобов'язані небудь робити спеціально для того , щоб попередити компілятор про перевантаження , просто використовуйте її . Компілятор розгадає , яку функцію слід використовувати , грунтуючись на пропонованих програмою параметрах.

Подібним чином наступна програма MSG_OVR.CPP перевантажує функцію show_message . Перша функція з ім'ям show_message виводить стандартне повідомлення , параметри їй не передаються. Друга виводить передане їй повідомлення , а третя виводить два повідомлення :

# include <iostream.h>

void show_message ( void )

{
cout << " Стандартне повідомлення : " << " Вчимося програмувати на C + + " << endl ;
}

void show_message ( char * message )

{
cout << message << endl ;
}

void show_message ( char * first , char * second )

{
cout << first << endl ;
cout << second << endl ;
}

void main ( void )

{
show_message ();
show_message ( "Вчимося програмувати на мові C + +! " ) ;
show_message ( " B C + + немає забобонів ! " , " Перевантаження - це круто !") ;
}

 

КОЛИ ПОТРІБНО Перевантажування

Одним з найбільш загальних випадків використання перевантаження є застосування функції для отримання певного результату , виходячи з різних параметрів . Наприклад, припустимо , що у вашій програмі є функція з ім'ям day_of_week , яка повертає поточний день тижня ( 0 для неділі , 1 для понеділка , ... , 6 для суботи). Ваша програма могла б перевантажити цю функцію таким чином , щоб вона вірно повертала день тижня , якщо їй переданий юліанський день в якості параметра , або якщо їй передані день , місяць і рік:

int day_of_week ( int julian_day )

{
/ / Оператори
}

int day_of_week ( int month , int day , int year )

{
/ / Оператори
}

 

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

Перевантаження функцій покращує читабельність програм

Перевантаження функцій C + + дозволяє вашим програмам визначати кілька функцій з одним і тим же ім'ям. Перевантажені функції повинні повертати значення однакового типу * , але можуть відрізнятися кількістю і типом параметрів . До появи перевантаження функцій в C + + програмісти мови С повинні були створювати кілька функцій з майже однаковими іменами. На жаль програмісти , бажаючі використовувати такі функції , повинні були пам'ятати , яка комбінація параметрів відповідає якій функції . З іншого боку , перевантаження функцій спрощує завдання програмістів , вимагаючи , щоб вони пам'ятали тільки одне ім'я функції .

* Перевантажені функції не зобов'язані повертати значення однакового типу з тієї причини , що компілятор однозначно ідентифікує функцію по її імені і набору її аргументів. Для компілятора функції з однаковими іменами , але різними типами аргументів - різні функції, тому тип значення - прерогатива кожної функції . - Прім.пер .

ЩО ВАМ ТРЕБА ЗНАТИ

Перевантаження функцій дозволяє вам вказати кілька визначень для однієї і тієї ж функції . У процесі компіляції C + + визначить , яку функцію слід використовувати, грунтуючись на кількості і типі переданих параметрів . З даного уроку ви дізналися, що перевантажувати функції досить просто . З уроку 14 ви дізнаєтеся , як посилання C + + спрощують процес зміни параметрів всередині функцій . Однак, перш ніж перейти до уроку 14 , переконайтеся , що ви вивчили такі основні концепції:

Перевантаження функцій надає кілька " поглядів " на одну і ту ж функцію всередині вашої програми .

Для перевантаження функцій просто визначте декілька функцій з одним і тим же ім'ям і типом значення, що повертається , які відрізняються тільки кількістю і типом параметрів .

У процесі компіляції C + + визначить , яку функцію слід викликати , грунтуючись на кількості і типі переданих параметрів .

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

Бажання написати дану статтю з'явилося після прочитання поста Перевантаження C + + операторів , тому що в ньому не було розкрито багато важливі теми .

Найголовніше , що необхідно пам'ятати - перевантаження операторів , це всього лише більш зручний спосіб виклику функцій , тому не варто захоплюватися перевантаженням операторів. Використовувати її слід тільки тоді , коли це спростить написання коду. Але , не настільки, щоб це ускладнювало читання . Адже , як відомо , код читається набагато частіше , ніж пишеться . І не забувайте , що вам ніколи не дадуть перевантажити оператори в тандемі з вбудованими типами , можливість перевантаження є тільки для користувача типів / класів .

 

синтаксис перевантаження

Синтаксис перевантаження операторів дуже схожий на визначення функції з ім'ям operator @ , де @ - це ідентифікатор оператора ( наприклад +, - , << , >> ) . Розглянемо найпростіший приклад :

class Integer
{
private :
int value ;
public :
Integer ( int i ) : value ( i )
{ }
const Integer operator + ( const Integer & rv ) const {
return ( value + rv.value ) ;
}
} ;

 

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

У більшості випадків , оператори ( крім умовних ) повертають об'єкт , або посилання на тип , до якого відносяться його аргументи (якщо типи різні, то ви самі вирішуєте як інтерпретувати результат обчислення оператора).

Перевантаження унарних операторів

Розглянемо приклади перевантаження унарних операторів для певного вище класу Integer . Заодно визначимо їх у вигляді дружніх функцій і розглянемо оператори декремента і инкремента :

class Integer

{private :
int value ;
public :
Integer ( int i ) : value ( i )
{ }

/ / унарний +
friend const Integer & operator + ( const Integer & i ) ;

/ / унарний -
friend const Integer operator - ( const Integer & i ) ;

/ / префіксний інкремент
friend const Integer & operator + + ( Integer & i ) ;

/ / постфіксний інкремент
friend const Integer operator + + ( Integer & i , int ) ;

/ / префіксний декремент
friend const Integer & operator - ( Integer & i ) ;

/ / постфіксний декремент
friend const Integer operator - ( Integer & i , int ) ;
} ;

/ / унарний плюс нічого не робить.
const Integer & operator + ( const Integer & i ) {
return i.value ;
}

const Integer operator - ( const Integer & i ) {
return Integer ( - i.value ) ;
}

/ / префіксная версія повертає значення після инкремента
const Integer & operator + + ( Integer & i ) {
i.value + +;
return i ;
}

/ / Постфіксний версія повертає значення до инкремента
const Integer operator + + ( Integer & i , int ) {
Integer oldValue ( i.value ) ;
i.value + +;
return oldValue ;
}

/ / префіксная версія повертає значення після декремента
const Integer & operator - ( Integer & i ) {
i.value - - ;
return i ;
}

/ / Постфіксний версія повертає значення до декремента
const Integer operator - ( Integer & i , int ) {
Integer oldValue ( i.value ) ;
i.value - - ;
return oldValue ;
}

Тепер ви знаєте , як компілятор розрізняє префіксние і постфіксні версії декремента і инкремента . У разі , коли він бачить вираз + + i , то викликається функція operator + + ( a ) . Якщо ж він бачить i + + , то викликається operator + + ( a , int ) . Тобто викликається перевантажена функція operator + + , і саме для цього використовується фіктивний параметр int в постфіксной версії.

 

бінарні оператори

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

class Integer
{
private :
int value ;
public :
Integer ( int i ) : value ( i )
{ }
friend const Integer operator + ( const Integer & left , const Integer & right ) ;

friend Integer & operator + = ( Integer & left , const Integer & right ) ;

friend bool operator == ( const Integer & left , const Integer & right ) ;
} ;

const Integer operator + ( const Integer & left , const Integer & right ) {
return Integer ( left.value + right.value ) ;
}

Integer & operator + = ( Integer & left , const Integer & right ) {
left.value + = right.value ;
return left ;
}

bool operator == ( const Integer & left , const Integer & right ) {
return left.value == right.value ;
}

 

У всіх цих прикладах оператори перевантажуються для одного типу , однак , це необов'язково. Можна, наприклад , перевантажити додавання нашого типу Integer і визначеного за його подобою Float .

Аргументи і повертаються значення

 

Як можна було помітити , в прикладах використовуються різні способи передачі аргументів у функції і повернення значень операторів.

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

Тип значення залежить від суті оператора. Якщо оператор повинен повертати нове значення , то необхідно створювати новий об'єкт ( як у випадку бінарного плюса ) . Якщо ви хочете заборонити зміну об'єкта як l - value , то потрібно повертати його константним .
Для операторів присвоювання необхідно повертати посилання на змінений елемент . Також , якщо ви хочете використовувати оператор присвоєння в конструкціях виду ( x = y ) . F ( ) , де функція f ( ) викликається для для змінної x , після присвоювання їй y , то чи не повертайте посилання на константу , повертайте просто посилання .
Логічні оператори повинні повертати в гіршому випадку int , а в кращому bool .

Оптимізація значення, що повертається

При створенні нових об'єктів і повернення їх з функції слід використовувати запис як для вищеописаного прикладу оператора бінарного плюса.

return Integer ( left.value + right.value ) ;

Чесно кажучи , не знаю , яка ситуація актуальна для C + +11 , всі міркування далі справедливі для C + +98 .

На перший погляд , це схоже на синтаксис створення тимчасового об'єкта , тобто начебто б немає різниці між кодом вище і цим:

Integer temp ( left.value + right.value ) ;
return temp ;

 

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

особливі оператори

У C + + є оператори , що володіють специфічним синтаксисом і способом перевантаження. Наприклад оператор індексування []. Він завжди визначається як член класу і , так як мається на увазі поведінку индексируемого об'єкта як масиву , то йому слід повертати посилання .

оператор кома

У число «особливих» операторів входить також оператор кома . Він викликається для об'єктів , поряд з якими поставлена ​​кома (але він не викликається в списках аргументів функцій). Придумати осмислений приклад використання цього оператора не так-то просто . Хабраюзер AxisPod в коментарях до попередньої статті про перевантаження розповів про одне.

Оператор разименованія покажчика

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

оператор присвоювання

Оператор присвоювання обов'язково визначається у вигляді функції класу , тому що він нерозривно пов'язаний з об'єктом, що знаходиться зліва від "=" . Визначення оператора привласнення в глобальному вигляді зробило б можливим перевизначення стандартного поведінки оператора " =" . приклад :

class Integer
{
private :
int value ;
public :
Integer ( int i ) : value ( i )
{ }

Integer & operator = ( const Integer & right ) {
/ / перевірка на самопрісваіваніе
if ( this == & right ) {
return * this ;
}
value = right.value ;
return * this ;
}
} ;

 

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

Неперегружаемие оператори

Деякі оператори в C + + не перевантажуються в принципі. По всій видимості , це зроблено з міркувань безпеки.

Оператор вибору члена класу ". " .

Оператор разименованія покажчика на член класу ". * "

У С + + відсутня оператор піднесення до степеня (як у Fortran ) " ** " .

Заборонено визначати свої оператори ( можливі проблеми з визначенням пріоритетів ) .

Не можна змінювати пріоритети операторів

 

Рекомендації до форми визначення операторів

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

Роб Мюррей , у своїй книзі C + + Strategies and Tactics визначив наступні рекомендації з вибору форми оператора :

Оператор

Рекомендована форма

Всі унарні оператори

член класу

= ( ) [] -> -> *

Обов'язково член класу

+ = - = / = * = ^ = & = | = % = >> = << =
член класу

Решта бінарні оператори

Чи не член класу

Чому так ? По-перше , на деякі оператори спочатку накладено обмеження . Взагалі , якщо семантично немає різниці як визначати оператор , то краще його оформити у вигляді функції класу , щоб підкреслити зв'язок , плюс крім цього функція буде підставляється ( inline ) . До того ж , іноді може виникнути потреба в тому , щоб представити лівобічний операнд об'єктом іншого класу. Напевно , найяскравіший приклад - перевизначення << і >> для потоків введення / виводу.

Література Брюс Еккель - Філософія C + +. Введення в стандартний C + +.


Читайте також:

  1. Вид заняття: лекція
  2. Вид заняття: лекція
  3. Вид заняття: лекція
  4. Вид заняття: лекція
  5. Вид заняття: лекція
  6. Вступна лекція
  7. Вступна лекція 1. Методологічні аспекти технічного регулювання у
  8. Клітинна селекція рослин.
  9. Колекція фонограм з голосами осіб, які анонімно повідомляли про загрозу вибуху
  10. ЛЕКЦІЯ (4): Мануфактурний період світової економіки
  11. Лекція - Геополітика держави на міжнародній арені
  12. Лекція 02.04.2013




Переглядів: 722

<== попередня сторінка | наступна сторінка ==>
Лекція 16 | Лекція 18

Не знайшли потрібну інформацію? Скористайтесь пошуком google:

  

© studopedia.com.ua При використанні або копіюванні матеріалів пряме посилання на сайт обов'язкове.


Генерація сторінки за: 0.023 сек.