Preopterećenje operatora u C ++: osnove, primjeri

U bilo kojoj znanosti postoje standardne oznake koje olakšavaju razumijevanje ideja. Primjerice, u matematici to je umnožavanje, dijeljenje, zbrajanje i drugi simbolički zapis. Izraz (x + y * z) je mnogo lakše razumjeti nego "pomnožiti y, z i dodati u x". Zamislite, sve do XVI. Stoljeća matematika nije imala simboličku notaciju, svi izrazi su pisani verbalno kao da je riječ o umjetničkom tekstu s opisom. I uobičajeni znakovi operacija za nas pojavili su se kasnije. Teško je precijeniti značenje kratkog zapisa znakova. Na temelju ovih razmatranja, programski jezici su dodani preopterećenju operatora. Razmotrite primjer.


Primjer preopterećenja operatora

Gotovo kao i svaki drugi jezik, C ++ podržava mnoštvo operatora koji rade s tipovima podataka ugrađenim u standardni jezik. No, većina programa koristi posebne vrste za rješavanje određenih zadataka. Primjerice, složena matematika ili matematička algebra se implementira u program tako da predstavlja kompleksne brojeve ili matrice u prilagođenim C ++ vrstama. Ugrađeni operatori ne mogu distribuirati svoje radove i provoditi potrebne postupke nad određenim razredima, koliko god očigledno izgledali. Stoga, za dodavanje matrica, na primjer, obično se stvara zasebna funkcija. Očito, pozivanje sum_matrix (A, B) u kodu imat će manje jasan karakter od izraza A + B.
Razmotrite približnu klasu kompleksnih brojeva:

//zamislite kompleksni broj u obliku para brojeva ss pomičnim zarezom.
klasni kompleks {{12} dvostruki re, im;
javni:
kompleksni (dvostruki r, dvostruki i): re (r), im (i) {} //konstruktor
složeni operator + (kompleks); //sklop preopterećenja
složeni operator * (složen); //preopterećenje množenja
};

nevažeći glavni () {
kompleks a {1 2}, b {3}}, c {0}};
c = a + b;
c = a.operator + (b); ////operativna funkcija može se nazvati bilo koja funkcija, ovaj unos je ekvivalentan a + b
c = a * b + kompleksu
; //Izvođenje uobičajenih pravila prioriteta operacija zbrajanja i množenja
}

Slično tome, možete učiniti, na primjer, preopterećenje I /O operatora u C ++ i prilagoditi ih za izlaz takvih složenih struktura kao matrice.

Operatori dostupni za preopterećenje

Potpuni popis svih operatora za koje se može koristiti mehanizam preopterećenja:

+

*

/

39]

nova

%

^

i str.

|

~

!

=

& gt;

+ =

- =

* =

/=

^ =

i

| 62]

=

=

==

84]

! =

> =

i sl.

|

++

- *

,

- & gt;

novo []

obriši

obriši []

Kao što se može vidjeti iz tablice, preopterećenje je dopušteno za većinu jezičnih operatera. Nema potrebe preopterećivati ​​operatera. Ovo se radi isključivo zbog praktičnosti. Tako, primjerice, preopterećenje operatora u Javi nedostaje. I sada o tako važnom trenutku.

Operatori čije je preopterećenje zabranjeno

  • Dozvola za vidljivost - «::»;
  • Izbor člana je ".";
  • Odabir člana putem pokazivača na člana - ". *";
  • Trostruki uvjetni operator - «?:»;
  • veličina operatora;
  • Operator tipa.

Pravi operand podataka operatora je ime, a ne vrijednost. Stoga bi dopuštenje za njihovo preopterećenje moglo dovesti do pisanja mnogih dvosmislenih dizajna i uvelike bi otežalo život programera. Iako postoje mnogi programski jezici koji dopuštaju preopterećenje svih operatora - na primjer, preopterećenje Python operatora.


& lt; script type = "text /javascript" & gt;
može blockSettings2 = {blockId: "R-A-70350-2", renderTo: "yandex_rtb_R-A-70350-2", async: 0};

if (document.cookie.indexOf ("abmatch =") & gt; = 0) {
blockSettings2 = {blockId: "RA-70350-2", renderTo: "yandex_rtb_R-A-70350- 2 ", statId: 70350async: 0};
}

Funkcija (a, b, c, d, e) {a [c] = a [c] || [], a [c] .push (funkcija () {Ya .Context.AdvManager.render (blockSettings2)}), e = b.getElementsByTagName ("script") , d = b.createElement ("script"), d.type = "text /javascript", d.src = "//an.yandex.ru/system/context.js", d.async =! 0e.parentNode.insertBefore (d, e)} (ovaj, ovaj.dokument, "yandexContextAsyncCallbacks");

Ograničenja

Ograničenje preopterećenja operatera:

  • Ne možete promijeniti binarni operator na unarnom i obratno, jer ne možete dodati treći operand.
  • Ne možete stvoriti nove operatere osim onih koji su. Ovo ograničenjepromiče uklanjanje mnogih nejasnoća. Ako postoji potreba za novim operatorom, možete koristiti funkciju koja će izvesti željenu radnju za te svrhe.
  • Operatorska funkcija može biti član klase ili imati barem jedan tip argumenta. Iznimka su novi i brisanje operatora. Ovo pravilo zabranjuje promjenu značenja izraza ako ne sadrže korisnički definirane vrste objekata. Konkretno, ne možete stvoriti operatorsku funkciju koja će raditi samo s pokazivačima ili prisiliti operatera dodavanja da radi kao množenje. Iznimke su operatori "=", "& amp;" i "," za predmete klase.
  • Operatorska funkcija s prvim pojmom, koja pripada jednom od ugrađenih tipova podataka na jeziku C ++, ne može biti član klase.
  • Ime bilo koje operatorske funkcije započinje s ključnom riječi operator, nakon čega slijedi simbolička oznaka samog operatora.
  • Ugrađeni operatori su definirani na takav način da postoji veza između njih. Primjerice, sljedeći operatori su međusobno ekvivalentni: ++ x; x + = 1; x = x + 1. Nakon redefiniranja veza između njih neće se zadržati. O održavanju zajedničkog rada na sličan način s novim tipovima programera morat će se brinuti o sebi.
  • Kompajler ne može misliti. Izrazi z + 5 i 5 + z (gdje je z - kompleksni broj) kompilator će razmatrati na različite načine. Prvi je "kompleksni broj", a drugi je "broj + kompleks". Stoga za svaki izraz trebate definirati vlastitu izjavu o dodatku.
  • Pri traženju definicije operatora, prevodilac ne daje prednost bilo kojem članu funkcije klase, niti pomoćnim funkcijama,koji su definirani izvan razreda. Za kompilatora su jednaki.

Interpretacije binarnih i unarnih operatora.

Binarni operator se definira kao funkcija člana s jednom varijablom ili kao funkcija s dvije varijable. Za svaki binarni operator @ izraz a @ b @ vrijedi konstrukt:


& lt; script type = "text /javascript" & gt;
može blockSettings3 = {blockId: "R-A-70350-3", renderTo: "yandex_rtb_R-A-70350-3", async: 0};
blockSettings3 = {blockId: "RA-70350-3", renderTo: "yandex_rtb_R-A-70350-" 3 ", statId: 70350sync:! 0};
}

Funkcija (a, b, c, d, e) {a [c] = a [c] || [], a [c] .push (funkcija () {Ya .Context.AdvManager.render (blockSettings3)}), e = b.getElementsByTagName ("script") , d = b.createElement ("script"), d.type = "text /javascript", d.src = "//an.yandex.ru/system/context.js", d.async =! 0e.parentNode.insertBefore (d, e)} (ovaj, ovaj.dokument, "yandexContextAsyncCallbacks");

a.operator @ (b) ili operator @ (a, b).

Uzmimo primjer klase kompleksnih brojeva za definiranje operacija kao članova klase i pomoćnog.

klasni kompleks {{174} dvostruki re, im;
javni:
kompleks & amp; operator + = (kompleks z);
kompleks & amp; operator * = (kompleks z);
};
//pomoćne funkcije
složeni operator + (kompleksni z1 kompleks z2);
složeni operator + (kompleks z, dvostruki a);

Koji će od operatera biti odabrani i hoće li ga uopće odabrati, određuje se unutarnjim mehanizmima jezika, o čemu će se raspravljati u nastavku. Obično se to događa u skladu s vrstama.

Izbor, da opiše funkciju člana klase ili izvan nje - općenito, općenito, okus. U gornjem primjeru, načelo odabira bilo je kako slijedi: ako operacija promijeni lijevi operand (na primjer, a + = b), onda ga upiši unutar klase i koristi prijenos varijable na adresi za njegovu izravnu promjenu; ako operacija nije ništamodificira i jednostavno vraća novu vrijednost (na primjer, a + b) - izvan definicije klase.

Definicija preopterećenja unarnih operatora u C ++ događa se na isti način, s tom razlikom da su podijeljeni u dvije vrste:

  • operatora prefiksa, koji se nalazi u operandu, - @, na primjer, i ++. o Definirano kao a.operator @ () ili operator @ (aa);
  • postfix operator, smješten nakon operanda, - b @, na primjer, i ++. o Definirano kao b.operator @ (int) ili operator @ (b, int)

Na isti način kao i kod binarnih operatora, u slučaju kada je izjava operatora u klasi i izvan klase, izbor će se izvršiti mehanizmima C ++-a.

Pravila za odabir operatera

Neka binarni operator @ bude primijenjen na objekte x klase X i y razreda Y. Pravila za rezoluciju x @ y bit će kako slijedi:

  1. ako je X klasa, potražite unutar nje definiciju operatora @ kao termin X ili osnovnu klasu X;
  2. da se pogleda kontekst u kojem se nalazi izraz x @ y;
  3. ako X pripada imenskom prostoru N, potražite izjavu operatora N;
  4. Ako Y pripada imenskom prostoru M, potražite izjavu operatora M.

Ako je u 1-4 pronađeno više izjava operatora operatora @, izbor će se izvršiti prema pravilima dopuštenja preopterećenih funkcija.

Potraga za unarnim operatorima provodi se na potpuno isti način.

Utvrditi definiciju klasnog kompleksa

Sada ćemo konstruirati klasu kompleksnih brojeva na detaljniji načinpokazati niz prethodno objavljenih pravila.

klasni kompleks {
dvostruki re, im;
javni:
kompleks & amp; operator + = (kompleks z) {//radi s izrazima oblika z1 + = z2
re + = z.re;
im + = z.im;
vrati * ovo;
}
kompleks & amp; operator + = (double a) {//radi s izrazima oblika z1 + = 5;
re + = a;
vrati * ovo;
}
complex (): re

, im

{} //konstruktor za inicijalizaciju po defaultu. Dakle, svi deklarirani cijeli brojevi će imati početne vrijednosti (0 0)
kompleks (dvostruko r): re (r), im

{} //konstruktor čini izraz oblika kompleks z = 11 mogućim; ekvivalent rekordnog z = kompleksa
;
kompleks (dvostruko r, dvostruko i): re (r), im (i) {} //konstruktor
};
složeni operator + (kompleksni z1 kompleks z2) {//radi s izrazima oblika z1 + z2
kompleksa res = z1;
povratak res + = z2; //koristimo operator definiran kao funkciju člana
}
složenog operatora + (kompleks z, dvostruko a) {//obrađuje izraze oblika z + 2
kompleksa res = z;
povratak res + = a;
}
složeni operator + (double a, complex z) {//obrađuje izraze oblika 7 + z
kompleksa res = z;
povratak res + = a;
}
//


Kao što se može vidjeti iz koda, preopterećenje operatora ima prilično složen mehanizam koji može uvelike rasti. Međutim, takav detaljan pristup omogućuje preopterećenje čak i za vrlo složene strukture podataka. Na primjer, preopterećenje C ++ operatora u klasi predloška. Takvo stvaranje funkcija za svakoga i sve može biti zamorno i dovesti do pogrešaka. Na primjer, ako dodate treći tip razmatranih funkcija, tada ćete morati razmotriti operacije zbog kombinacije triju vrsta. Morat ćemo napisati 3 funkcije s jednim argumentom, 9 - s dva i 27 - s tri. Stoga, u nekim slučajevima, provedba svih tih funkcija i njihovo značajno smanjenjeKoličine se mogu postići pomoću konverzije tipova.

Specijalni operatori

Operator indeksiranja "[]" uvijek bi trebao biti definiran kao član klase, budući da smanjuje ponašanje objekta na niz. U ovom slučaju, argument za indeksiranje može biti bilo kojeg tipa, što omogućuje, na primjer, stvaranje asocijativnih nizova. Operator poziva funkcije (() može se smatrati binarnom operacijom. Na primjer, u konstruktu "izraz (popis izraza)" lijevi operand binarne operacije () bit će "izraz", a desni je popis izraza. Funkcija operator () () mora biti član klase. Operator slijeda "," (zarez) se poziva za objekte ako je zarez pored njih. Međutim, operator ne sudjeluje u prijenosu argumenata funkcije. Operator imenovanja "- & gt;" također mora biti definiran kao član funkcije. U svom sadržaju može se definirati kao unary postfix operator. U isto vrijeme, on mora nužno vratiti ili vezu ili pokazivač koji omogućuje pristup objektu. Operator dodjele je također definiran samo kao član klase zbog svoje veze s lijevim operandom. Operatori dodjele «=», adrese «&» i redoslijed «,» moraju se definirati u javnom bloku.

Ishod

Operativno preopterećenje pomaže u realizaciji jednog od ključnih aspekata PLO-a na polimorfizmu. Ali važno je razumjeti da preopterećenje nije ništa drugo nego drugi način pozivanja značajki. Zadatak preopterećenja operatera je često poboljšati razumijevanje koda nego osigurati pobjedu u bilo kojem problemu.
I to nije sve. Također, treba imati na umu da je preopterećenje operatora složen mehanizam s mnoštvom zamki. Stoga je vrlo lako napraviti pogrešku. To je glavni razlog zbog kojeg većina programera savjetuje da se suzdrže od zagušenja operatora i pribjegavaju mu samo u ekstremnim slučajevima i uz puno povjerenje u njihove postupke.

Preporuke

  • Izvršite preopterećenje operatora samo za simulaciju običnog zapisa. Da bi se pročitao kod. Ako kod postaje složeniji u smislu strukture ili čitljivosti, trebali biste napustiti preopterećenje operatora i koristiti funkcije.
  • Za velike operande, da bi se uštedio prostor, upotrijebite argumente za unos stalnih referenci za prijenos.
  • Optimizirajte povratne vrijednosti.
  • Ne dodirujte postupak kopiranja ako je prikladan za vaš razred.
  • Ako zadana kopija nije prikladna, izmijenite ili izričito zabranite kopiranje.
  • Funkcije članstva trebaju imati prednost u odnosu na nečlanske funkcije u slučajevima kada funkcije zahtijevaju pristup predstavljanju klase.
  • Navesti prostor imena i naznačiti odnos između funkcija klase.
  • Koristiti nečlanske funkcije za simetrične operatore.
  • Koristite operator () za indekse u višedimenzionalnim nizovima.
  • Budite oprezni s implicitnim transformacijama.
  • Povezane publikacije