muzruno.com

Претоварване на оператора в C ++: основи, примери

Във всяка наука съществуват стандартни обозначения, които улесняват разбирането на идеите. Например, в математиката е умножение, разделяне, добавяне и друга символна нотация. Изразът (x + y * z) е много по-лесен за разбиране, отколкото "умножете y, c, z и добавете към x". Представете си, че преди шестнадесети век математиката не е имала символични означения, всички изрази са писани устно, сякаш са художествен текст с описание. Обичайната нотация за операции се появи по-късно. Смисълът на краткия запис на персонажи е трудно да се надцени. Въз основа на тези съображения, оперативните претоварвания са добавени към езиците за програмиране. Помислете за примера.

Пример за претоварване от оператора

Почти като всеки език C ++ поддържа много оператори, които работят с типове данни, вградени в езиковия стандарт. Но повечето програми използват персонализирани типове за решаване на определени задачи. Например, сложна математика или матрична алгебра се реализират в програмата поради представянето на комплексни числа или матрици под формата на потребителски дефинирани типове C ++. Вградените оператори не знаят как да разпределят работата си и да изпълняват необходимите процедури по класове потребители, колкото и очевидни да изглеждат те. Следователно, за да добавите матрици, обикновено обикновено се създава отделна функция. Очевидно обаждането към sum_matrix (A, B) в кода ще бъде по-малко ясно от израза A + B.

Представяне на сложен номер

Помислете за приблизителен клас сложни числа:

// комплексно число представени като двойка плаващ tochkoy.class комплекс {двойно повторно, IM-публичен: комплекс (двойна R, двойно I): Re (R), IM (I) {} // konstruktorcomplex оператор + (комплекс) - // претоварване slozheniyacomplex оператор * (комплекс) - // претоварване умножение} -void основен () {комплекс {1, 2} б {3, 4}, в {0, 0} -С = а + бв = a.operator + (б) - //// функция оператор може да бъде причинено от всяка функция, влизането е еквивалентно на + бв = а * в + комплекс (1, 3) - // изпълнява нормални приоритетните правила за събиране и умножение операции}

По същия начин, можете например да претоварите I / O оператори в C ++ и да ги адаптирате към извеждането на такива сложни структури като матрици.

Оператори са на разположение за претоварване

Пълен списък на всички оператори, за които можете да използвате механизма за претоварване:

+

;

*

/

%

^

|

~

!

=

<

+=

-=

* =

/ =

% =

^ =

=

| =

<<

> =

<<=

==

!=

<=

=

||

++



-;

-> *

,

->

[]

()

нов

нов []

изтривам

изтриване []

Както можете да видите от таблицата, претоварването е приемливо за повечето оператори на езици. Операторът не може да бъде претоварен. Това се прави само за удобство. Поради това не е налице претоварване от оператор в Java например. И сега за следващия важен момент.

Операторите не са претоварени

  • Разделителната способност на обхвата е "::";
  • Изборът на член е ".";
  • Избирането на член чрез указател на член е ". *";
  • Треньорският условен оператор е "?:";
  • Размерът на оператора;
  • Типичният оператор.

Правилният операнд на тези оператори е името, а не стойността. Следователно, разрешаването на тяхното претоварване може да доведе до написването на множество двусмислени конструкции и до голяма степен усложнява живота на програмистите. Въпреки че има много програмни езици, в които всички оператори имат право да претоварват - например, претоварват операторите на Python.

ограничения

Ограничения за претоварване на оператора:

Списък на операторите, които са на разположение за претоварване
  • Не можете да променяте двоичен оператор на unary оператор и обратно, точно както не можете да добавите трети операнд.
  • Не можете да създавате нови оператори освен тези, които са налични. Това ограничение премахва множество неясноти. Ако има нужда от нов оператор, можете да използвате за тази цел функция, която ще изпълни необходимите действия.
  • Функцията на оператора може да бъде член на класа или да има поне един аргумент от тип, дефиниран от потребителя. Изключенията са новите оператори и операторите за изтриване. Това правило забранява промяната на значението на изразите в случай, че те не съдържат типове обекти, дефинирани от потребителя. По-конкретно, не можете да създадете операторска функция, която работи изключително с указатели или кара допълнителния оператор да работи като умножение. Изключение са операторите "=", "" и "," за класовите обекти.
  • Операторът функционира с първия член, принадлежащ към един от вградените типове данни на езика C ++, не може да бъде член на класа.
  • Името на всяка операционна функция започва с оператора на ключова дума, последван от символното обозначение на самия оператор.
  • Вградените оператори са дефинирани по такъв начин, че има връзка между тях. Например, следните оператори са равностойни един на друг: ++ x- x + = 1 - x = x + 1. След предефиниране връзката между тях няма да бъде запазена. Запазването на тяхната работа в екип по този начин с нови типове програмист ще трябва да се погрижи отделно.
  • Компилаторът не знае как да мисли. Изразите z + 5 и 5 + z (където z е сложен номер) ще бъдат третирани различно от компилатора. Първият е "сложен + номер", а вторият е "номер + комплекс". Следователно, за всеки израз трябва да определите свой собствен оператор за добавяне.
  • При търсене на определението за оператор, компилаторът не дава приоритет на функциите на членовете на класа, нито на помощните функции, които са определени извън класа. За съставителя те са еднакви.

Тълкуване на двоични и unary оператори.

Двойна операция

Двумерен оператор се дефинира като членна функция с една променлива или като функция с две променливи. За всеки двоичен оператор @ следните конструкции са валидни в израза a @ b, @:

a.operator @ (b) или оператор @ (a, b).

Помислете за примера на класа с комплексни числа, дефиницията на операциите като членове на класовете и помощните такива.

клас комплекс {двойно ре, im-обществено: комплекс оператор + = (комплекс z) -комплекс оператор * = (комплекс z) -} - // помощни функции комплект оператор + (комплекс z1, комплекс z2) -комплекс оператор + (комплекс z, двоен a) -

Кой от операторите ще бъде избран и дали изобщо ще бъде избран, се определя от вътрешните механизми на езика, които ще бъдат разгледани по-долу. Обикновено това се случва според съответствието на типа.

Изборът, описвайки функцията като член на класа или извън него, е въпрос по принцип на вкус. В горния пример, на принципа на подбор е, както следва: ако операцията променя левия операнд (например, + = б), а след това го запишете в един клас и да използвате променлива трансмисия при, за непосредствена Changes-, ако операцията не променя нищо и просто се връща новата стойност ( например, a + b) е извън определението на класа.

Единични и двоични операции

Определянето на претоварването на unary оператори в C ++ се извършва по подобен начин, с разликата, че те са разделени на два типа:

  • оператор на префикс, разположен преди операнда, - @a, например, ++ i. о е дефиниран като a.operator @ () или оператор @ (aa);
  • postfix оператор, намиращ се след операнда - b @, например, i ++. о е дефиниран като b.operator @ (int) или оператор @ (b, int)

По същия начин, както при двоичните оператори за случая, когато изявлението на оператора е в класа и извън класа, изборът ще бъде направен от механизмите на C ++.

Правила за избор на оператор

Видове C ++ оператори

Нека двойният оператор @ да бъде приложен към обектите x от класа X и y от класа Y. Правилата за разрешаване на x @ y ще бъдат както следва:

  1. ако X е клас, погледнете вътре в него за дефиниция на оператор @ като член на X или базов клас X;
  2. разглежда контекста, в който се намира изразът x @ y;
  3. ако X се отнася до пространството на име N, потърсете декларацията на оператора в N;
  4. ако Y се отнася до пространството за имена M, потърсете изявлението на оператора в M.

В случай, че са открити няколко съобщения на оператор оператор @ в 1-4, изборът ще бъде направен в съответствие с правилата за разрешаване на претоварените функции.

Търсенето на едни оператори е точно същото.

Изяснено определение на сложните

Сега ще изградим по-детайлен клас от сложни числа, за да демонстрираме редица предишни правила.

клас комплекс {двойно ре, im-обществено: комплекс оператор + = (комплекс z) {// работи с изрази на формуляра z1 + = z2re + = z.re-im + = z.im-return * this-} комплекс оператор + = (двойно a) {// работи с изрази на формуляра z1 + = 5-re + = a-return * this-} комплекс (): re (0), im , По този начин, всички декларирани комплексни числа ще имат първоначални стойности (0, 0) комплексни (двойни r): re (r), im (0) {} (11) комплекс _L U (двойна R, двойно I): Re (R), IM (I) {} // конструктор} комплекс _L U оператор + (комплекс Z1, Z2 комплекс) {// работи с изразяване на форма Z1 на + z2complex ВЕИ = z1 възвратни ВЕИ + = Z2- // използване оператор определя като функция член} комплекс оператор + (комплекс Z, двойно а) {// дръжка изразяване на формата Z + 2complex ВЕИ = Z-връщане ВЕИ + = а-} комплекс оператор + (double a, complex z) {// обработва изрази на формата 7 + zcomplex res = z-връщане res + = a -} // hellip-

Както виждате от кода, претоварването на оператора има много сложен механизъм, който може да расте много. Този подробен подход обаче позволява претоварване дори при много сложни структури от данни. Например, претоварване на C ++ отчети в класа шаблон.

Такова създаване на функции за всеки и всичко може да бъде досадно и да доведе до грешки. Ако например добавите трети тип към разглежданите функции, ще трябва да разгледате операциите по причини, свързани с комбинация от три типа. Необходимо е да напишете 3 функции с един аргумент, 9 - с два и 27 - с три. Ето защо в редица случаи изпълнението на всички тези функции и значително намаляване на техния брой може да се постигне чрез използването на преобразувания на типа.

Специални оператори

Индексният оператор "[]" трябва винаги да бъде дефиниран като член на класа, тъй като той намалява поведението на обекта към масив. Аргументът за индексиране може да бъде от всякакъв вид, който позволява създаването например на асоциативни масиви.

Операторът за извикване на функция "()" може да се разглежда като двоична операция. Например, в конструкцията на израза (expression list), левият операнд на двоичната операция () е израз, а десният операнд е списък с изрази. Функцията оператор () () трябва да бъде член на класа.

Операторът на поредицата "," (запетая) се извиква за обекти, ако има запетая до тях. Операторът обаче не участва в изброяването на аргументите за функциите.

Операторът за дереференцииране "->" също трябва да бъде определен като член на функцията. В смисъла му, той може да бъде дефиниран като unary postfix оператор. В този случай тя задължително трябва да върне или връзка, или указател, който позволява достъп до обекта.

Оператор на задание Той също така се определя само като член на класа поради връзката му с левия операнд.

Операторите на задачите "=", адресите "" и последователностите "," трябва да бъдат дефинирани в публичния блок.

Резултатът

свръхтовар операторите помагат за изпълнението на един от ключовите аспекти на ООП за полиморфизма. Но е важно да се разбере, че претоварването не е нищо повече от различен начин за призоваване на функциите. Задачата за претоварване на операторите често е да се подобри разбирането на кода, а не да се гарантират печалби по някои въпроси.

Какво представлява полиморфизмът?

И това не е всичко. Също така трябва да се има предвид, че претоварването на операторите е сложен механизъм с много клопки. Ето защо е много лесно да направите грешка. Това е основната причина, поради която повечето програмисти съветват да се въздържат от претоварване от оператора и да прибягват до него само като последна инстанция и с пълна увереност в действията си.

препоръки

Създателят на C ++ Bjarne Stroustrup
  1. Извършвайте претоварване от оператора само, за да симулирате познат запис. За да стане кодът по-разбираем. Ако кодът става по-сложен по отношение на структурата или четливостта, трябва да избягвате претоварването на операторите и да използвате функции.
  2. За големи операнди, за да се спести място, използвайте аргументи с типа постоянни препратки към прехвърлянето.
  3. Оптимизирайте връщащите стойности.
  4. Не докосвайте операцията за копиране, ако тя е подходяща за вашия клас.
  5. Ако по подразбиране копирането не работи, променете или изрично забранявате копирането.
  6. Трябва да предпочитате функциите на членовете по функции, които не са членове, в случаите, когато функциите изискват достъп до класовото представяне.
  7. Посочете пространство на имена и обозначавате връзката на функциите с техния клас.
  8. Използвайте функции, които не са членове, за симетрични оператори.
  9. Използвайте оператора () за индекси в многомерни масиви.
  10. Внимавайте с имплицитни реализации.
Споделяне в социалните мрежи:

сроден