Supraîncărcarea operatorului în C ++: elementele de bază, exemple

În orice știință există denumiri standard care facilitează înțelegerea ideilor. De exemplu, în matematică este înmulțire, diviziune, plus și altă notație simbolică. Expresia (x + y * z) este mult mai ușor de înțeles decât "multiplicarea y, c, z și adăugarea la x". Imaginați-vă că, înainte de secolul al XVI-lea, matematica nu avea denumiri simbolice, toate expresiile au fost scrise verbal ca și cum ar fi un text artistic cu o descriere. Notă obișnuită pentru operațiuni a apărut mai târziu. Semnificația unei înregistrări de caractere scurte este greu de supraestimat. Pe baza acestor considerente, au fost adăugate supraîncărcări ale operatorilor în limbile de programare. Luați în considerare exemplul.

Exemplu de supraîncărcare a operatorului

Aproape ca orice limbă, C ++ suportă mulți operatori care lucrează cu tipuri de date încorporate în standardul de limbă. Dar majoritatea programelor utilizează tipuri personalizate pentru a rezolva anumite sarcini. De exemplu, matematică complexă sau matrice algebră sunt realizate în program datorită reprezentării numerelor complexe sau a matricelor sub forma tipurilor definite de utilizator de C ++. Operatorii încorporați nu știu cum să distribuie munca lor și să efectueze procedurile necesare pe clasele de utilizatori, oricât de evident ar părea. Prin urmare, pentru a adăuga matrice, de exemplu, o funcție separată este de obicei creată. Evident, apelul la sum_matrix (A, B) din cod va fi mai puțin clar decât expresia A + B.

Reprezentarea unui număr complex

Luați în considerare o clasă aproximativă de numere complexe:

// număr complex reprezentat ca o pereche de plutitoare tochkoy.class complex {double re, im publice: complex (r dublu, dublu i): re (r), im (i) {} // operatorul konstruktorcomplex + (complex) - // suprasarcina operatorului slozheniyacomplex * (complex) - // multiplicare de suprasarcină} -void main () {complex a {1, 2}, b {3, 4}, c {0, 0} -c = a + bc = a.operator + (b) - //// funcția operatorului poate fi cauzată de orice funcție, intrarea este echivalentă cu o + bc = a * b + complex (1, 3) - // executa reguli de prioritate normală a operațiilor de adunare și înmulțire}

În mod similar, puteți, de exemplu, să supraîncărcați operatorii de I / O din C ++ și să-i adaptați la ieșirea unor structuri complexe, cum ar fi matricele.

Operatorii disponibili pentru suprasarcină

O listă completă a tuturor operatorilor pentru care puteți utiliza mecanismul de suprasarcină:

+

;

*

/

%

^

| |

~

!

=

<

+=

-=

* =

/ =

% =

^ =

=

| =

<<

> =

<<=

==

!=

<=

=

||

++



-;

-> *

,

->

[]

()

nou

nou []

șterge

ștergeți []

După cum puteți vedea din tabel, supraîncărcarea este acceptabilă pentru majoritatea operatorilor de limbi. Operatorul nu poate fi supraîncărcat. Acest lucru se face exclusiv pentru comoditate. De aceea, supraîncărcarea operatorului în Java, de exemplu, nu este disponibilă. Și acum despre următorul moment important.

Operatorii care nu sunt supraîncărcați

  • Rezoluția domeniului este "::";
  • Alegerea unui membru este ".";
  • Selectarea unui membru prin intermediul unui pointer membru este ". *";
  • Operatorul condițional ternar este "?:";
  • Dimensiunea operatorului;
  • Operatorul de tip.

Operandul corect al acestor operatori este numele, nu valoarea. Prin urmare, rezolvarea supraîncărcării lor ar putea duce la scrierea unei multitudini de construcții ambigue și ar complica foarte mult viața programatorilor. Deși există multe limbi de programare în care toți operatorii au voie să suprascrie - de exemplu, supraîncărcarea operatorilor Python.

restricţii

Restricții de suprasarcină ale operatorului:

Diagrama operatorilor disponibili pentru supraîncărcare
  • Nu puteți schimba un operator binar la un operator unar și invers, așa cum nu puteți adăuga un al treilea operan.
  • Nu puteți crea alte noi operatori decât cele disponibile. Această restricție elimină o multitudine de ambiguități. Dacă este nevoie de un nou operator, puteți utiliza în acest scop o funcție care va efectua acțiunea necesară.
  • Funcția operator poate fi fie membru al clasei, fie are cel puțin un argument de tip definit de utilizator. Excepțiile sunt operatorii noi și eliminați. Această regulă interzice modificarea semnificației expresiilor în cazul în care acestea nu conțin tipuri de obiecte definite de utilizator. În particular, nu puteți crea o funcție de operator care funcționează exclusiv cu indicatori sau determină ca operatorul de adunare să funcționeze ca multiplicare. Excepția este operatorii "=", "" și "," pentru obiectele de clasă.
  • Operatorul care funcționează cu primul membru aparținând unuia dintre tipurile de date încorporate din limba C ++ nu poate fi membru al clasei.
  • Numele oricărei funcții a operatorului începe cu operatorul de cuvinte cheie, urmat de desemnarea simbolică a operatorului însuși.
  • Operatorii încorporați sunt definiți în așa fel încât să existe o legătură între ele. De exemplu, următorii operatori sunt echivalenți unul cu altul: ++ x- x + = 1- x = x + 1. După redefinire, relația dintre ele nu va fi păstrată. Menținerea muncii lor în echipă în acest fel cu noi tipuri de programatori va trebui să fie tratată separat.
  • Compilatorul nu știe cum să gândească. Expresiile z + 5 și 5 + z (unde z este un număr complex) vor fi tratate diferit de compilator. Primul este "complex + număr", iar al doilea este "număr + complex". Prin urmare, pentru fiecare expresie, trebuie să definiți propriul operator de adăugare.
  • La căutarea definiției unui operator, compilatorul nu acordă prioritate funcțiilor membre ale clasei și nici funcțiilor auxiliare definite în afara clasei. Pentru compilator ele sunt egale.

Interpretarea operatorilor binari și unari.

Operație binară

Un operator binar este definit ca o funcție membră cu o variabilă sau ca o funcție cu două variabile. Pentru orice operator binar @, următoarele construcții sunt valide în expresia a @ b, @:

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

Luați în considerare exemplul unei clase de numere complexe definirea operațiunilor ca membri ai clasei și a celor auxiliare.

clasa complexă {double re, im-public: complex operator + = (complex z) -complex operatorul * = (z complex) -} - // operatorul funktsiicomplex auxiliar + (z1 complex, z2 complex) Operatorul -complexul + (complex z, double a) -

Care dintre operatorii va fi selectat și dacă acesta va fi ales, este determinat de mecanismele interne ale limbii, care vor fi discutate mai jos. De obicei, acest lucru se întâmplă prin potrivirea tipului.

Alegerea de a descrie funcția ca membru al unei clase sau în afara ei - este, în general, gustul. In exemplul de mai sus, principiul de selecție a fost după cum urmează: în cazul în care operațiunea modifică operandul din stânga (de exemplu, a + b =), atunci înregistrați într-o clasă și de a folosi transmisie variabilă la, pentru Changes- sa imediată în cazul în care operațiunea nu se schimbă nimic și doar returnează noua valoare ( de exemplu, a + b) depășește definiția clasei.

Operații unare și binare

Determinarea suprasarcinii operatorilor unari în C ++ are loc într-un mod similar, cu diferența că sunt împărțiți în două tipuri:

  • operatorul de prefix, situat înaintea operandului, - @a, de exemplu, ++ i. o este definit ca a.operator @ () sau operator @ (aa);
  • operatorul postfix situat după operand - b @, de exemplu, i ++. o este definit ca b.operator @ (int) sau operator @ (b, int)

În același mod ca și cu operatorii binari pentru cazul în care instrucțiunea operatorului se află atât în ​​clasă, cât și în afara clasei, alegerea se va face prin mecanismele C ++.

Regulile de selectare a operatorilor

Tipuri de operatori C ++

Fie operatorul binar @ aplicat obiectelor x din clasa X si y din clasa Y. Regulile pentru rezolvarea x @ y vor fi urmatoarele:

  1. dacă X este o clasă, uită-te în interiorul ei pentru definirea operatorului @ ca membru al lui X sau a clasei de bază X;
  2. vizualizați contextul în care este localizată expresia x @ y;
  3. dacă X se referă la spațiul de nume N, căutați declarația operatorului în N;
  4. dacă Y se referă la spațiul de nume M, căutați declarația operatorului în M.

În cazul în care mai multe anunțuri ale operatorului de operator @ au fost găsite în 1-4, alegerea se va face în conformitate cu regulile de rezolvare a funcțiilor supraîncărcate.

Căutarea operatorilor unari este exact aceeași.

Definirea clară a complexului

Acum vom construi o clasă de numere complexe într-un mod mai detaliat pentru a demonstra un număr de reguli exprimate anterior.

clasa complexă {double re, im-public: complex operator + = (complex z) {// funcționează cu expresii ale formularului z1 + = z2re + = z.re-im + = z.im-return * this-} complex operatorul + = (dublu a) {// operează cu expresii ale z1 formă + = 5-re + = a-return * this-} complex (): re (0) im (0) {} // constructor implicit pentru inițializarea . Astfel, toate numerele complexe declarate vor avea valorile inițiale (0, 0) complex (double r): re (r), im (0) {} // constructor permite o expresie a formei complexe z = 11- echivalent de înregistrare z = complex (11) -complexul (r dublu, dublu i): re (r), im (i) {} // constructor} operatorul -complexul + (z1 complex, z2 complex) {// operează cu expresii ale z1 formă + z2complex res = res z1-retur + = operatorul utilizare // Z2 este definit ca funcție membru} operator de complex + (complex z, double a) {// expresii mâner ale formei res z + 2complex res = z-retur + = a-} operator de complex + (double a, z complex) {// procesează expresiile formei 7 res + zcomplex res = z-retur + = a -} // hellip-

După cum puteți vedea din cod, supraîncărcarea operatorului are un mecanism foarte complex, care poate crește foarte mult. Cu toate acestea, o astfel de abordare detaliată permite supraîncărcarea chiar și pentru structurile de date foarte complexe. De exemplu, suprasolicitarea instrucțiunilor C ++ din clasa de șabloane.

O astfel de creare a funcțiilor pentru toată lumea și totul poate fi obositoare și poate duce la erori. De exemplu, dacă adăugați un al treilea tip la funcțiile considerate, va trebui să luați în considerare operațiunile din motive de combinare a trei tipuri. Este necesar să scrieți 3 funcții cu un argument, 9 - cu două și 27 - cu trei. Prin urmare, într-o serie de cazuri, implementarea tuturor acestor funcții și o reducere semnificativă a numărului acestora pot fi obținute prin utilizarea conversiilor de tip.

Operatori speciali

Operatorul de index "[]" trebuie să fie întotdeauna definit ca membru al clasei, deoarece reduce comportamentul obiectului la o matrice. Argumentul de indexare poate fi de orice tip, care permite crearea, de exemplu, a matricelor asociative.

Operatorul pentru a apela funcția "()" poate fi considerat o operație binară. De exemplu, în construcția expresiei (listă de expresii), operandul stâng al operației binare () este o expresie, iar operandul drept este o listă de expresii. Funcția operator () () trebuie să fie un membru al clasei.

Operatorul de secvență "," (virgulă) este apelat pentru obiecte dacă există o virgulă lângă ele. Cu toate acestea, operatorul nu participă la enumerarea argumentelor funcției.

Operatorul de dereferențiere "->" trebuie de asemenea definit ca membru al funcției. În sensul său, acesta poate fi definit ca un operator postfix unar. În acest caz, trebuie să reveniți în mod obligatoriu fie la un link, fie la un pointer care permite accesul la obiect.

Operator de atribuire De asemenea, este definit doar ca membru al clasei datorită relației sale cu operanul stâng.

Operatorii de asignare "=", adresele "" și secvențele "," trebuie să fie definite în blocul public.

Rezultatul

suprasarcină operatorii ajută la punerea în aplicare a unuia dintre aspectele-cheie ale OOP privind polimorfismul. Dar este important să înțelegem că supraîncărcarea nu este mai mult decât o altă modalitate de a apela funcții. Sarcina de a supraîncărca operatorii este de multe ori să îmbunătățească înțelegerea codului, mai degrabă decât să asigure câștigurile în anumite probleme.

Ce este polimorfismul?

Și asta nu e tot. De asemenea, trebuie să se țină cont de faptul că supraîncărcarea operatorului este un mecanism complex, cu multe capcane. Prin urmare, este foarte ușor să faci o greșeală. Acesta este motivul principal pentru care majoritatea programatorilor sfătuiesc să se abțină de la utilizarea supraîncărcării de către operatori și să recurgă la acestea doar ca o ultimă soluție și cu încredere deplină în acțiunile lor.

recomandări

C ++ Creatorul Bjarne Stroustrup
  1. Efectuați supraîncărcarea operatorului numai pentru a simula o înregistrare cunoscută. Pentru a face codul mai ușor de citit. În cazul în care codul este mai complexă în structura sau lizibilitatea, ar trebui să renunțe la utilizarea Reacoperirea și funcția.
  2. Pentru operanzii mari, pentru a economisi spațiu, utilizați argumente cu tipul de referințe constante de transfer.
  3. Optimizați valorile retur.
  4. Nu atingeți operația de copiere dacă este potrivită pentru clasa dvs.
  5. Dacă copierea implicită nu funcționează, modificați sau interziceți în mod explicit copierea.
  6. Ar trebui să preferați funcțiile membrilor față de funcțiile non-membre în cazurile în care funcțiile necesită acces la o reprezentare de clasă.
  7. Specificați un spațiu de nume și indicați relația funcțiilor cu clasa lor.
  8. Utilizați funcții non-membre pentru operatorii simetrici.
  9. Utilizați operatorul () pentru indexurile din matrice multidimensionale.
  10. Aveți grijă cu conversiile implicite.
Distribuiți pe rețelele sociale:

înrudit
Ce este un div în Pascal? Adăugări, calcule și exempleCe este un div în Pascal? Adăugări, calcule și exemple
Limbaj de programare JavaLimbaj de programare Java
Cum schimb limba în "MacBook"? mijloaceCum schimb limba în "MacBook"? mijloace
Exemplu de împărțire a unui număr cu un număr. Tabel de diviziuneExemplu de împărțire a unui număr cu un număr. Tabel de diviziune
Limbi formale și naturale: exempleLimbi formale și naturale: exemple
Cele mai simple operații logice din domeniul informaticiiCele mai simple operații logice din domeniul informaticii
Ce pot fi atribuite limbilor formale? Exemple de utilizareCe pot fi atribuite limbilor formale? Exemple de utilizare
Interpretată este ... Interpretul este un sinonimInterpretată este ... Interpretul este un sinonim
Factorial în Pascal: cum se calculează. Probe de probaFactorial în Pascal: cum se calculează. Probe de proba
Istoria dezvoltării limbajelor de programare: pe scurt despre totIstoria dezvoltării limbajelor de programare: pe scurt despre tot
» » Supraîncărcarea operatorului în C ++: elementele de bază, exemple