Operațiuni de supraîncărcare
În C ++, definiți seturi de operații pe variabilele tipurilor standard, cum ar fi +. -. *. / etc Fiecare operație poate fi aplicată operatorilor de un anumit tip.
Din păcate, numai un număr limitat de tipuri sunt suportate direct de orice limbaj de programare. De exemplu, C și C ++ nu au permisiunea de a efectua operații cu numere complexe, matrici, șiruri de caractere, seturi. Cu toate acestea, toate aceste operațiuni pot fi efectuate prin clase în C ++.
Să presupunem că seturile A și B sunt date:
A = <а1, а2, а3>;
B =
și dorim să realizăm operațiuni de îmbinare (+) și intersecții (*) de seturi.
Puteți defini clasa Set - "set" și defini operațiile pe obiecte din această clasă, exprimându-le folosind semne de operare care există deja în C ++, de exemplu, + și *. Ca rezultat, operațiunile + și * pot fi utilizate ca mai înainte, și le pot oferi și funcții suplimentare (îmbinări și intersecții). Cum să determinați ce funcție trebuie să îndeplinească operatorul: vechi sau nou? Foarte simplu - de tipul de operandi. Și cum rămâne cu prioritatea operațiunilor? Prioritatea predefinită a operațiunilor este reținută. Pentru a extinde funcționarea operației la noi tipuri de date, trebuie să definiți o funcție specială numită "funcția operator". Formatul său:
Dacă este necesar, se poate adăuga un prototip:
Dacă presupunem că operator_option operator este numele unei anumite funcții, atunci prototipul și definiția funcției de operare sunt similare cu prototipul și definiția funcției obișnuite a limbajului C ++. Operația definită în acest mod se numește supraîncărcare.
Suprasolicitarea operațiunilor unare
Orice operație unară @ poate fi definită în două moduri: fie ca o funcție componentă fără parametri, fie ca o funcție globală (posibil prietenoasă) cu un singur parametru. În primul caz, expresia @ Z înseamnă Z.operator @ (). în al doilea, apel operator @ (Z).
Operațiile unare care sunt supraîncărcate într-o anumită clasă pot fi supraîncărcate numai printr-o funcție componentă non-statică fără parametri. Obiectul de clasă numit este tratat automat ca un operand.
Operațiile unare care sunt supraîncărcate în afara ariei de clasă (ca funcții globale) trebuie să aibă un parametru al tipului de clasă. Obiectul trecut prin acest parametru este perceput ca un operand.
a) în primul caz (descriere în aria de clasă):
b) în cel de-al doilea caz (descriere în afara ariei de clasă):
Supraîncărcarea operațiunilor binare
Orice operație binară @ poate fi definită în două moduri: fie ca o funcție componentă cu un parametru, fie ca o funcție globală (posibil prietenoasă) cu doi parametri. În primul caz, x @ y înseamnă a apela x.operator @ (y). în al doilea, operatorul de apel @ (x, y).
Operațiile care sunt supraîncărcate într-o clasă pot fi supraîncărcate numai prin funcții componente non-statice cu parametri. Obiectul de clasă apelat este recunoscut automat ca primul operand.
Operațiile care sunt supraîncărcate în afara zonei de clasă trebuie să aibă două operanzi, dintre care unul trebuie să aibă un tip de clasă.
Operațiuni de supraîncărcare ++ și -.
Procedurile de incrementare incrementală ++ și decrementare există în două forme: prefix și postfix. În specificația C ++ modernă, există o modalitate prin care compilatorul poate distinge între cele două forme. În conformitate cu această metodă, sunt specificate două versiuni ale operatorului de funcții ++ () și operator - (). Acestea sunt definite după cum urmează:
Specificarea parametrului int pentru formularul postfix nu specifică al doilea operand, ci este folosit numai pentru a-l deosebi de forma prefixului.
Supraîncărcarea apelului funcției
Aceasta este o operație '()'. Este o operație binară. Primul operand este de obicei un obiect de clasă, al doilea este o listă de parametri.
Supraîncărcarea operației de atribuire
Operațiunea se caracterizează prin trei caracteristici:
- operațiunea nu este moștenită;
- operația este definită implicit pentru fiecare clasă ca operație de copiere bițială a obiectului din dreapta semnului de operare, la obiectul din stânga.
- Operația poate fi supraîncărcată numai în zona de definire a clasei. Acest lucru asigură că primul operand este întotdeauna o expresie validă la stânga.
Clasa personalizată este String:
Care este eroarea aici? Când obiectul s1 este atribuit obiectului s2. indicatorul p al obiectului s2 pornește îndreptat către aceeași zonă de memorie ca și indicatorul p al obiectului s1. Astfel, atunci când aceste obiecte sunt șterse, memoria indicată de pointerul p al obiectului s1. se eliberează de două ori și memoria indicată de indicatorul p al obiectului s2 înainte de alocare. nu a fost eliberat deloc.
Deși în acest exemplu această eroare nu este periculoasă, în programe reale cu alocare dinamică a memoriei, aceasta poate provoca caderea programului.
În acest caz, trebuie să supraîncărcați singur operația de atribuire. Să arătăm cum să facem acest lucru pentru clasa noastră String.
Apoi, se verifică dacă este suficientă memorie în obiectul din stânga caracterului de atribuire a obiectului din dreapta caracterului de atribuire. Dacă nu este suficient, memoria este eliberată și este alocată o nouă dimensiune necesară. Apoi linia este copiată în această memorie.
Operațiunea supraîncărcată.
Operațiunea nouă. Setarea implicită poate fi în două forme:
Prima formă nu este folosită pentru matrice, a doua pentru matrice.
Operația supraîncărcată nouă poate fi definită în următoarele forme, respectiv pentru non-matrice și matrice:
Primul și singurul argument necesar t trebuie întotdeauna să aibă tipul size_t. Dacă argumentul este de tipul size_t. atunci argumentul sizeof (t) este înlocuit automat în noua funcție. și anume devine o valoare egală cu mărimea obiectului t în octeți.
De exemplu, să presupunem că este dată următoarea funcție:
și se numește după cum urmează:
Aici t = dublu, n = 5.
Ca urmare, după apel, valoarea t în corpul funcției va fi egală cu sizeof (dublu).
Când supraîncărcați noua operație, apar mai multe operații globale noi. dintre care unul este definit în limba implicită, în timp ce altele sunt supraîncărcate. Se pune întrebarea: cum se face distincția între astfel de operațiuni? Aceasta se face prin modificarea numărului și a tipurilor de argumente. Dacă se modifică numai tipurile de argumente, poate apărea o ambiguitate, care este o consecință a eventualelor transformări ale acestor tipuri una la cealaltă. Dacă apare această ambiguitate, trebuie să specificați explicit un tip atunci când apelați nou, de exemplu:
Unul dintre motivele pentru care noua operație este supraîncărcată. este dorința de a da o semantică suplimentară, de exemplu, furnizând informații de diagnosticare sau rezistență la eșecuri. În plus, clasa poate oferi o schemă de alocare a memoriei mai eficientă decât cea oferită de sistem.
În conformitate cu standardul C ++ din fișierul antet
Aceste funcții utilizează generarea de aruncări și un alocator separat.
Versiunea cu nothrow alocă memoria ca de obicei, dar dacă selecția nu reușește, ea returnează 0. și nu este generată de bad_alloc. Acest lucru ne permite să folosim strategia de gestionare a erorilor pentru alocarea memoriei până când nu este aruncată o excepție.
Reguli de utilizare a operațiunii noi
- Obiectele organizate cu ajutorul noului au o durată de viață nelimitată. Prin urmare, zona de memorie trebuie eliberată de operatorul de ștergere.
- Dacă memoria este rezervată pentru o matrice, noua operație returnează un pointer la primul element al matricei.
- Atunci când se rezervă memoria pentru o matrice, toate dimensiunile trebuie să fie exprimate ca valori pozitive.
- Arhivele nu pot fi inițializate.
- Obiectele de clasă pot fi organizate utilizând noua operație. dacă clasa are un constructor implicit.
- Link-urile nu pot fi organizate folosind noua operație, deoarece memoria nu le este alocată.
- Noua operație în sine calculează cerința de memorie pentru tipul de date organizat, astfel încât primul parametru al operației este întotdeauna de tip size_t.
Eroare la manipularea operațiunii noi.
Efectul de eroare al noii operațiuni are loc în două etape:
- Se determină ce funcții sunt furnizate pentru tratarea erorilor. Funcțiile proprii trebuie să fie de tip new_handler și create folosind funcția set_new_handler. Fișierul new.h este declarat
- Se numește funcția new_handler corespunzătoare. Această funcție trebuie:
- sau apelați o excepție bad_alloc;
- sau finalizați programul;
- fie eliberați memoria și încercați să o distribuiți din nou.
Clasa de diagnosticare bad_alloc este declarată în new.h.
Implementarea BC ++ include o variabilă globală specială _new_handler, a cărei valoare este un indicator pentru funcția new_handler, care este executată atunci când eșuează. În mod implicit, dacă noua operație nu poate aloca cantitatea necesară de memorie, se generează o excepție numită bad_alloc. Inițial, această excepție a fost numită xalloc și a fost definită în fișierul exception.h. Excepția xalloc continuă să fie folosită în multe compilatoare. Cu toate acestea, este înlocuită de numele rău definit în standardul C ++.
Să luăm în considerare câteva exemple.
Exemplul 1. În exemplu, utilizați blocul de încercare. captura face posibilă controlul unei încercări nereușite de alocare a memoriei.
Exemplul 2. Deoarece în exemplul anterior, când se lucrează în condiții normale, este puțin probabilă o eroare de alocare a memoriei, în acest exemplu este forțată eroarea de alocare a memoriei. Procesul de alocare a memoriei continuă până la apariția unei erori.
Exemplul 3. Este prezentată forma supraîncărcată a operațiunii noi - operațiunea nouă (notă).
Exemplul 4. Se demonstrează diferite forme de supraîncărcare a funcționării noi.
Supraîncărcarea operației de ștergere.
Funcția de ștergere a funcției este de două tipuri:
Al doilea formular include un argument de tip size_t. transmis prin ștergere. Aceasta este transmisă compilatorului ca mărimea obiectului indicat de p.
Funcția de supraîncărcare a operației de ștergere este aceea că operațiile de ștergere globală nu pot fi supraîncărcate. Ele pot fi reîncărcate numai în raport cu clasa.