Preobremenitev operaterjev v C ++: osnove, primeri

V vsaki znanosti obstajajo standardni zapisi, ki olajšujejo razumevanje idej. Na primer, v matematiki gre za množenje, delitev, dodatek in drugo simbolno notacijo. Izraz (x + y * z) je veliko lažje razumeti kot "pomnožiti y, z in dodati v x". Predstavljajte si, da do XVI. Stoletja matematika ni imela simbolne oznake, vsi izrazi so bili napisani verbalno, kot da je umetniško besedilo z opisom. In navadni znaki delovanja za nas so se pojavili kasneje. Težko je preceniti pomen kratkega zapisa znakov. Na podlagi teh premislekov so programski jeziki dodali preobremenitvi operaterjev. Razmislite o primeru.


Primer preobremenitve operaterja

Skoraj kot vsak jezik C ++ podpira številne operaterje, ki delajo s podatkovnimi tipi, vgrajenimi v standardni jezik. Vendar večina programov uporablja posebne vrste za reševanje določenih nalog. Na primer, kompleksna matematika ali matrična algebra se izvaja v programu tako, da predstavlja kompleksne številke ali matrike v meri po meri C ++. Vgrajeni operaterji ne morejo distribuirati svojega dela in izvajati potrebnih postopkov nad določenimi razredi, ne glede na to, kako očitni so. Zato se pri dodajanju matrik običajno ustvari ločena funkcija. Očitno bo klic sum_matrix (A, B) v kodi manj jasen kot izraz A + B.
Razmislite o približnem razredu kompleksnih števil:

//zamislite kompleksno število v obliki para števil zplavajoči vejici.
razredni kompleks {{12} dvojni re, im;
javno:
kompleksno (dvojno r, dvojno i): re (r), im (i) {} //konstruktor
kompleksni operator + (kompleks); //preobremenitveni sklop
kompleksni operator * (kompleksen); //preobremenitev množenja
};

prazen glavni () {
kompleks a {1 2}, b {3}}, c {0}};
c = a + b;
c = a.operator + (b); ////operacijsko funkcijo lahko imenujemo poljubna funkcija, ta vnos je enakovreden a + b
c = a * b + kompleksu (1 3); //Izvedemo običajna pravila o prednostih operacij seštevanja in množenja
}

Podobno lahko naredimo, na primer, preobremenitve I /O operaterjev v C ++ in jih prilagodimo izhodom tako kompleksnih struktur kot matrik.

Upravljavci, ki so na voljo za preobremenitev

Popoln seznam vseh izvajalcev, za katere se lahko uporabi mehanizem za preobremenitev:

+

*

/

39]

nova

novo []

%

^

in str.

|

~

!

=

& gt;

+ =

- =

* =

/=

^ =

in

| 62]

=

=

==

84]

! =

> =

in str.

|

++

- *

,

- & gt;

izbriši

delete []

Kot je razvidno iz tabele, je preobremenitev dovoljena za večino operaterjev. Operaterja ni treba preobremeniti. To je zgolj zaradi udobja. Zato je na primer preobremenitev operaterjev v Javi odsotna. In zdaj o tako pomembnem trenutku.

Operaterji, katerih preobremenitev je prepovedana

  • Dovoljenje za vidljivost - «::»;
  • Izbira člana je ".";
  • Izbira člana s kazalcem na člana - ". *";
  • Ternarni pogojni operater - «?:»;
  • velikost operaterja;
  • Upravljavec tipa.

Pravi operand podatkov operaterjev je ime, ne vrednost. Zato bi dovoljenje za njihovo preobremenitev lahko pripeljalo do pisanja številnih dvoumnih modelov in bi močno otežilo življenje programerjev. Čeprav obstaja veliko programskih jezikov, ki omogočajo preobremenitev vseh operaterjev - na primer preobremenitev Python operaterjev.


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

če (document.cookie.indexOf ("abmatch ="))> = 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)} (to, ta.dokument, "yandexContextAsyncCallbacks");

Omejitve

Omejevanje preobremenitve upravljavca:

  • Binarnega operaterja ne morete spremeniti na unarnem in obratno, saj ne morete dodati tretjega operanda.
  • Ne morete ustvariti novih operaterjev, razen tistih, ki so. Ta omejitevspodbuja odpravo številnih dvoumnosti. Če potrebujete novega operaterja, lahko uporabite funkcijo, ki bo za te namene izvedla želeno dejanje.
  • Operatorska funkcija je lahko član razreda ali ima vsaj en tip argumenta. Izjema so novi in ​​izbrisani operaterji. To pravilo prepoveduje spreminjanje pomena izrazov, če ne vsebujejo uporabniško definiranih vrst objektov. Zlasti ne morete ustvariti operaterjeve funkcije, ki bi delovala samo s kazalci ali prisilila operaterja dodatka, da deluje kot množenje. Izjeme so operatorji "=", "& amp;" in "," za predmete razreda.
  • Operativna funkcija s prvim izrazom, ki pripada enemu od vgrajenih tipov podatkov v jeziku C ++, ne more biti član razreda.
  • Ime katere koli operacijske funkcije se začne s ključnim operaterjem, ki mu sledi simbolična oznaka samega operaterja.
  • Vgrajeni upravljavci so opredeljeni tako, da obstaja povezava med njimi. Naslednji operaterji so na primer enakovredni: ++ x; x + = 1; x = x + 1. Po ponovni določitvi povezave med njima ne bo ohranjena. O ohranjanju njihovega dela na podoben način z novimi vrstami programerjev bodo morali skrbeti zase.
  • Prevajalnik ne more razmišljati. Izrazi z + 5 in 5 + z (kjer je z - kompleksno število) bo prevajalnik obravnaval na različne načine. Prva je "kompleksna + številka", druga pa "kompleksna številka +". Zato morate za vsak izraz definirati lastno izjavo o dodatku.
  • Pri iskanju definicije operatorja prevajalnik ne daje prednosti nobenemu funkcijskemu članu razreda, niti pomožnim funkcijam,ki so opredeljene izven razreda. Za prevajalnik so enaki.

Razlaga binarnih in unarnih operaterjev.

Binarni operator je opredeljen kot funkcija člana z eno spremenljivko ali kot funkcija z dvema spremenljivkama. Za vsak binarni operator @ je izraz a @ b @ veljaven konstrukt:


& lt; script type = "text /javascript" & gt;
lahko 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: 70350async: 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)} (to, ta.dokument, "yandexContextAsyncCallbacks");

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

Razmislite primer razreda kompleksnih števil za definicijo operacij kot članov razreda in pomožnega.

Razredni kompleks {{174} dvojni re, im;
javna:
kompleksna; operator + = (kompleksno z);
kompleksni & amp; operator * = (kompleksno z);
};
//pomožne funkcije
kompleksni operator + (kompleksno z1 kompleksno z2);
kompleksni operator + (kompleksno z, dvojno a);

Kateri od operaterjev bo izbran in ali bo sploh izbran, je določen z notranjimi mehanizmi jezika, ki bodo obravnavani spodaj. Običajno se to zgodi v skladu s tipi.

​​

Izbira, ki opisuje funkcijo člana razreda ali zunaj njega - na splošno okus. V zgornjem primeru je bilo načelo izbire naslednje: če operacija spremeni levi operand (npr. A + = b), ga zapišite znotraj razreda in uporabite prenos spremenljivke na naslov za njegovo neposredno spremembo; če operacija ni ničspremeni in preprosto vrne novo vrednost (na primer a + b) - po definiciji razreda.

Opredelitev preobremenitve unarnih operaterjev v C ++ se pojavlja na enak način, z razliko, da so razdeljeni na dva tipa:

  • predpono operaterja, ki se nahaja na operandu, - @, na primer, i ++. o Definiran kot a.operator @ () ali operator @ (aa);
  • postfix operater, ki se nahaja za operandom, - b @, na primer, i ++. o Opredeljeno kot b.operator @ (int) ali operator @ (b, int)

Tako kot pri binarnih operaterjih, v primeru, ko je izjava operatorja v razredu in zunaj razreda, bo izbira izvedena z mehanizmi C ++.

Pravila za izbiro operaterja

Naj bo binarni operator @ uporabljen za objekte x razreda X in y razreda Y. Pravila za ločljivost x @ y bodo naslednja:

  1. če je X razred, v njem poiščite definicijo operatorja @ kot izraz X ali osnovni razred X;
  2. , da si ogledajo kontekst, v katerem se nahaja izraz x @ y;
  3. če X pripada imenskem prostoru N, poiščite izjavo operaterja N;
  4. Če Y pripada imenskem prostoru M, poiščite izjavo operatorja M.

Če je bilo v 1-4 najdenih več izjav operatorjevega operaterja @, bo izbira izvedena v skladu s pravili dovoljenja preobremenjenih funkcij.

Iskanje unarnih operaterjev se izvaja na povsem enak način.

Izboljšati definicijo razrednega kompleksa

Zdaj bomo konstruirali razred kompleksnih številk na bolj podroben način.pokazati številna predhodno objavljena pravila.

razredni kompleks {
dvojno ponovno, im;
javna:
kompleksna & amp; operator + = (kompleks z) {//deluje z izrazi oblike z1 + = z2
re + = z.re;
im + = z.im;
vrni * to;
}
kompleksen & amp; operator + = (double a) {//deluje z izrazi oblike z1 + = 5;
re + = a;
vrne * to;
}
kompleks (): re

, im

{} //konstruktor za privzeto inicializacijo. Tako bodo vsa deklarirana celoštevilčna števila imela začetne vrednosti (0 0)
kompleksne (dvojne r): re (r), im

{} //konstruktor naredi izraz oblike kompleksno z = 11; enakovredna zapisu z = kompleks
;
kompleks (dvojno r, dvojno i): re (r), im (i) {} //konstruktor
};
kompleksni operater + (kompleksno z1 kompleksno z2) {//deluje z izrazi oblike z1 + z2
kompleksa res = z1;
vrne res + = z2; //uporabimo operator, definiran kot funkcijo člana
}
kompleksni operator + (kompleksno z, dvojno a) {//obravnava izraze oblike z + 2
kompleksa res = z;
vrne res + = a;
}
kompleksni operator + (double a, complex z) {//obdeluje izraze oblike 7 + z
kompleksa res = z;
vrne res + = a;
}
//


Kot je razvidno iz kodeksa, ima preobremenjenost operaterjev precej zapleten mehanizem, ki lahko močno raste. Vendar pa takšen podroben pristop omogoča preobremenitev tudi za zelo kompleksne strukture podatkov. Na primer, preobremenitev operaterjev C ++ v razredu predloge. Takšno ustvarjanje funkcij za vse in vse je lahko dolgočasno in vodi do napak. Če na primer dodate tretji tip obravnavanih funkcij, boste morali upoštevati operacije zaradi kombinacije treh vrst. Morali bomo napisati 3 funkcije z enim argumentom, 9 - z dvema in 27 - s tremi. Zato je v nekaterih primerih izvajanje vseh teh funkcij in njihovo znatno zmanjšanjeKoličine se lahko dosežejo s pretvorbo tipov.

Posebni operatorji

Operator indeksiranja "[]" mora biti vedno opredeljen kot član razreda, ker zmanjšuje obnašanje objekta na matriko. V tem primeru je lahko argument za indeksiranje poljuben tip, ki omogoča izdelavo na primer asociativnih nizov. Operator klica funkcije (() se lahko šteje za binarno operacijo. Na primer, v konstruktu "izraz (seznam izrazov)" bo levi operand binarne operacije () "izraz", desno pa je seznam izrazov. Funkcija operator () () mora biti član razreda. Operator zaporedja "," (vejica) se imenuje za objekte, če je poleg njih vejica. Vendar pa operater ne sodeluje pri prenosu argumentov funkcije. Upravljavec poimenovanja "-" mora biti definiran tudi kot član funkcije. V svoji vsebini ga lahko definiramo kot uarni postfix operater. Hkrati mora nujno vrniti povezavo ali kazalec, ki omogoča dostop do objekta. Operator dodelitve je prav tako definiran samo kot član razreda zaradi svoje povezave z levim operandom. V javnem bloku morajo biti definirani operatorji dodelitve «=», naslovi «&» in zaporedje «,».

Izid

Preobremenitev operaterja pomaga uresničiti enega od ključnih vidikov PLO na področju polimorfizma. Vendar je pomembno razumeti, da preobremenitev ni nič drugega kot drug način klicanja funkcij. Naloga preobremenitve operaterjev je pogosto izboljšanje razumevanja kodeksa kot zagotovitev zmagovalnih težav.
In to ni vse. Prav tako je treba upoštevati, da je preobremenitev operaterjev zapleten mehanizem z množico pasti. Zato je zelo enostavno narediti napako. To je glavni razlog, zakaj večina programerjev svetuje, da se vzdrži prezasedenosti operaterja in se zateka k njej le v skrajnih primerih in s popolnim zaupanjem v njihova dejanja.

Priporočila

  • Izvedite preobremenitev operaterja samo za simulacijo navadnega zapisa. Da bi naredili kodo za branje. Če koda postane težja zaradi strukture ali berljivosti, zavrnite preobremenitev operaterjev in uporabo funkcij.
  • Za velike operande, da bi prihranili prostor, uporabite argumente za vnos s stalnimi referencami za prenos.
  • Optimizirajte povratne vrednosti.
  • Ne dotikajte se postopka kopiranja, če je primeren za vaš razred.
  • Če privzeta kopija ni primerna, spremenite ali izrecno prepovejte kopiranje.
  • Funkcijam članstva je treba dati prednost pred nečlanskimi funkcijami, kadar funkcije zahtevajo dostop do predstavitve razreda.
  • Navedite imenski prostor in navedite razmerje med funkcijami razreda.
  • Uporabite nečlanske funkcije za simetrične operaterje.
  • Uporabite operator () za indekse v večdimenzionalnih nizih.
  • Uporabljajte previdnost pri implicitnih transformacijah.
  • Sorodne publikacije