Alti operatori
Pentru a evita obezitatea finala care ameninta cartea, nu au fost tratate doua grupuri de operatori. Primul grup este format din operatorii pe biti, care permit manipularea bitilor individuali într-o valoare; acesti operatori au fost mosteniti din C. Al doilea consta din cei doi operatori de dereferentiere a membrilor; acestia sunt contributii C++. Aceasta anexa descrie pe scurt cele doua grupe de operatori.
Operatori pe biti
Operatorii pe biti opereaza asupra bitilor unei valori întregi. De exemplu, operatorul de deplasare la stânga muta bitii la stânga, iar operatorul de negare pe biti schimba fiecare unu cu zero si fiecare zero cu unu. Cu totul, în C++ exista sase asemenea operatori: << >> & si
Operatorii de deplasare
Operatorul de deplasare la stânga are urmatoarea sintaxa:
valoare << deplasare
Aici, valoare este întregul care trebuie deplasat, iar deplasare este numarul de biti de deplasat. De exemplu:
13 << 3
înseamna deplasarea tuturor bitilor din valoarea 13 cu trei pozitii la stânga. Pozitiile ramase libere se completeaza cu zerouri, iar bitii care ies în afara sunt eliminaţ 20520x2320u ;i (vezi Figura E.1).
Valoarea 13 este stocata ca un int pe doi octeti:
0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1
0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0
Biti eliminati Deplasarea la stânga Biti eliberati
a lui 13 cu 3 biti: 13 << 3 completati cu zerouri
Figura E.1 Operatorul de deplasare la stânga.
Deoarece fiecare pozitie a unui bit reprezinta o valoare de doua ori mai mare decât a bitului din dreapta sa (vezi Anexa A), deplasarea cu o pozitie la stânga este echivalenta cu înmultirea valorii cu 2. Analog, deplasarea cu doua pozitii este echivalenta cu înmultirea cu 22, iar deplasarea cu n pozitii este echivalenta cu înmultirea cu 2n.
Operatorul de deplasare la stânga ofera o abilitate întâlnita adesea în limbajele de asamblare. Totusi, un operator de deplasare la stânga al unui limbaj de asamblare modifica direct continutul unui registru, în timp ce operatorul din C++ produce o noua valoare fara a modifica valorile existente. Observati urmatorul exemplu:
int x = 20;
int y = x << 3;
Acest cod nu modifica valoarea lui x. Expresia x << 3 foloseste valoarea lui x pentru a calcula o noua valoare, la fel cum x + 3 are ca rezultat o noua valoare fara a-l afecta pe x
Daca doriti sa modificati valoarea unei variabile folosind operatorul de deplasare la stânga, trebuie sa folositi si o atribuire. Puteti utiliza atribuirea obisnuita sau operatorul <<=, care combina deplasarea cu atribuirea.
x = x << 4; // atribuire normala
y <<= 2; // deplasare si atribuire
Dupa cum poate va asteptati, operatorul de deplasare la dreapta (>>) deplaseaza bitii la dreapta. Are urmatoarea sintaxa:
valoare >> deplasare
Aici, valoare este valoarea întreaga care este deplasata, iar deplasare este numarul de pozitii de deplasat. De exemplu:
17 >> 2
înseamna deplasarea tuturor bitilor în valoarea 17 cu doua locuri la dreapta. Pentru întregii fara semn, locurile eliberate sunt completate cu zerouri, iar bitii deplasati în afara sunt eliminati. Pentru întregii cu semn, locurile eliberate sunt completate cu zerouri sau cu valoarea originala a bitului cel mai din stânga. Alegerea depinde de implementare (vezi Figura E.2 pentru un exemplu în care completarea se face cu zerouri).
Valoarea 13 este stocata ca un int pe doi octeti:
0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1
Biti eliberati Deplasarea la dreapta Biti eliminati
completati cu zerouri a lui 13 cu 3 biti: 13 >> 3
Figura E.2 Operatorul de deplasare la dreapta.
Deplasarea cu o pozitie la dreapta este echivalenta cu o împartire întreaga la 2. În general, deplasarea cu n pozitii la dreapta este echivalenta cu o împartire întreaga la 2n.
C++ defineste de asemenea un operator de deplasare la dreapta si atribuire, pentru cazul în care doriti sa înlocuiti valoarea unei variabile cu valoarea deplasata:
int q = 43;
q >>= 2; // inlocuieste 43 cu 43 >> 2, adica 10
În unele sisteme de calcul, utilizarea operatorilor de deplasare la stânga si la dreapta este mai rapida decât înmultirea sau împartirea la 2 cu operatorii obisnuiti, dar, o data cu îmbunatatirea compilatoarelor, aceste diferente sunt tot mai nesemnificative.
Operatorii logici pe biti
Operatorii logici pe biti sunt analogi cu operatorii logici normali, exceptând faptul ca se aplica unei valori bit cu bit si nu ca unui tot unitar. De exemplu, sa luam operatorul de negare normal ( ) si cel de negare pe biti ( ). Operatorul converteste o valoare true (sau diferita de zero) în false iar o valoare false în una true. Operatorul converteste fiecare bit în parte în opusul sau (1 în 0 si 0 în 1). Sa luam ca exemplu valoarea unsigned char
unsigned char x = 3;
Expresia !x are valoarea 0. Pentru a vedea valoarea lui ~x, îl scrieti în notatie binara: 00000011. Apoi convertiti fiecare 0 în 1 si fiecare 1 în 0. Rezulta valoarea 11111100, sau în baza 10, valoarea 252 (vezi Figura E.3 pentru un exemplu pe 16 biti).
Valoarea 13 este stocata ca un int pe doi octeti:
0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1
Valoarea ~13 - fiecare 1 devine 0, iar fiecare 0 devine 1
1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0
Figura E.3 Operatorul de negare pe biti.
Operatorul SAU pe biti ( ) combina doua valori întregi pentru a obtine o valoare întreaga noua. Fiecare bit din noua valoare este 1 daca unul sau altul, sau amândoi, bitii corespunzatori din valorile initiale este 1. Daca amândoi bitii corespunzatori sunt 0, atunci bitul rezultat este setat tot la 0 (vezi Figura E.4).
a 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1
b 1 0 1 0 0 1 0 0 0 0 0 0 0 1 1 0
a | b 1 0 1 0 0 1 0 0 1 0 0 0 1 1 1 1
1 deoarece 0 deoarece 1 deoarece 1 deoarece
bitul corespunzator bitii corespunzatori bitul corespunzator bitii corespunzatori
din b este 1 din a si din b sunt 0 din a este 1 din a si din b sunt 1
Figura E.4 Operatorul SAU pe biti.
Tabelul E.1 rezuma modul în care operatorul combina bitii.
Tabelul E.1 Valoarea b1 ¦ b2
Valoarea bitilor b1 = 0 b1 = 1
b2 = 0 0 1
b2 = 1 1 1
Operatorul SAU exclusiv pe biti ( ) combina doua valori întregi pentru a obtine o valoare întreaga noua. Fiecare bit din noua valoare este 1 daca unul sau altul, dar nu amândoi, bitii corespunzatori din valorile initiale este 1. Daca amândoi bitii corespunzatori sunt 0, sau amândoi sunt 1 atunci bitul rezultat este setat la 0 (vezi Figura E.5).
a 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1
b 1 0 1 0 0 1 0 0 0 0 0 0 0 1 1 0
a ^ b 1 0 1 0 0 1 0 0 1 0 0 0 1 0 1 1
1 deoarece 0 deoarece 1 deoarece 0 deoarece
bitul corespunzator bitii corespunzatori bitul corespunzator bitii corespunzatori
din b este 1 din a si din b sunt 0 din a este 1 din a si din b sunt 1
Figura E.5 Operatorul SAU exclusiv pe biti.
Tabelul E.2 rezuma modul în care operatorul combina bitii.
Tabelul E.2 Valoarea b1 ^ b2
Valoarea bitilor b1 = 0 b1 = 1
b2 = 0 0 1
b2 = 1 1 0
Operatorul sI pe biti (&) combina doua valori întregi pentru a obtine o valoare întreaga noua. Fiecare bit din noua valoare este 1 numai daca amândoi bitii corespunzatori din valorile initiale sunt 1. Daca oricare sau amândoi bitii corespunzatori sunt 0, atunci bitul rezultat este setat tot la 0 (vezi Figura E.6).
a 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1
b 1 0 1 0 0 1 0 0 0 0 0 0 0 1 1 0
a & b 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 deoarece numai unul din 0 deoarece 1 deoarece
bitii corespunzatori bitii corespunzatori bitii corespunzatori
este 1 din a si din b sunt 0 din a si din b sunt 1
Figura E.6 Operatorul sI pe biti.
Tabelul E.3 rezuma modul în care operatorul & combina bitii.
Tabelul E.3 Valoarea b1 & b2
Valoarea bitilor b1 = 0 b1 = 1
b2 = 0 0 0
b2 = 1 0 1
Câteva tehnici de baza pentru manipularea bitilor
Adesea, controlul hardware presupune activarea si dezactivarea unor biti, precum si verificarea starii acestora. Operatorii pe biti ofera posibilitatea realizarii unor astfel de actiuni. Vom trece rapid în revista aceste metode.
În exemplele urmatoare multibiti reprezinta o valoare generala, iar bit reprezinta valoarea corespunzatoare unui anumit bit. Bitii sunt numerotati de la dreapta la stânga, începând cu bitul 0, deci valoarea corespunzatoare bitului din pozitia n este 2n. De exemplu, un întreg în care singurul bit 1 este bitul numarul 3, are valoarea 23 sau 8. În general, fiecare bit individual corespunde unei puteri a lui 2, asa cum am descris în Anexa A. Deci vom folosi denumirea de bit pentru a reprezenta o putere a lui 2; adica un anumit bit este setat la valoarea 1 iar ceilalti la 0.
Setarea unui bit la valoarea 1
Urmatoarele operatii realizeaza setarea bitului din multibiti care este corespunzator valorii bit, la valoarea 1:
multibiti = multibiti ¦ bit;
multibiti ¦= bit;
Fiecare operatie seteaza valoarea bitului corespunzator la valoarea 1 indiferent de valoarea sa initiala. Acest lucru se întâmpla deoarece 1 ¦ 0 este 1, la fel cum 1 ¦ 1 este tot 1. Ceilalti biti din multibiti ramân nemodificati. Acest lucru se întâmpla deoarece 0 ¦ 0 este 0, iar 0 ¦ 1 este 1.
Comutarea unui bit
Urmatoarele operatii realizeaza comutarea bitului din multibiti care este corespunzator valorii bit. Adica, îl pune pe 1 daca era 0 si îl pune pe 0 daca era 1:
multibiti = multibiti ^ bit;
multibiti ^= bit;
1 ^ 0 este 1, activând un bit inactiv, iar 1 ^ 1 este 0, dezactivând un bit activ. Ceilalti biti din multibiti ramân nemodificati. Acest lucru se întâmpla deoarece 0 ^ 0 este 0, iar 0 ^ 1 este 1.
Setarea unui bit la valoarea 0
Urmatoarea operatie seteaza bitul din multibiti care corespunde valorii bit la valoarea 0:
multibiti = multibiti & ~bit;
Aceasta instructiune pune bitul respectiv pe valoarea 0, indiferent de valoarea sa initiala. Mai întâi, operatia ~bit are ca rezultat un întreg în care toti bitii sunt 1 în afara celui care era initial 1; acesta devine în schimb 0. 0 & 0, ca si 0 & 1 are rezultatul 0. Deci, bitul respectiv este dezactivat. Ceilalti biti din multibiti ramân nemodificati. Acest lucru se întâmpla deoarece 1 & orice bit are ca rezultat valoarea initiala a bitului respectiv.
Iata o modalitate mai rapida pentru a realiza acelasi lucru:
multibiti &= ~bit;
Testarea valorii unui bit
Sa presupunem ca doriti sa aflati daca bitul din multibiti care corespunde lui bit este 1. Urmatorul test nu este corect:
if (multibiti == bit) // nu e buna
Chiar daca bitul corespunzator este 1, mai pot exista si alti biti cu valoarea 1. Egalitatea de mai sus este adevarata doar daca numai bitul corespunzator este 1. Remediul este efectuarea mai întâi a operatiei multibiti & bit. Astfel, rezulta o valoare care are bitii 0 pe toate celelalte pozitii, deoarece 0 & orice valoare este 0. Ramâne nemodificat doar bitul care corespunde valorii bit, deoarece 1 & orice valoare nu modifica acea valoare. Prin urmare testul corect este:
if (multibiti & bit == bit) // testarea unui bit
Operatori pentru dereferentierea membrilor
Înainte de a trata operatorii pentru dereferentierea membrilor, trebuie sa discutam putin bazele acestor operatori. C++ va permite sa definiti pointeri spre membrii unei clase, dar acest proces nu e deloc simplu. Pentru a vedea ce presupune, sa studiem un exemplu de clasa care pune unele probleme:
class exemplu
Sa presupunem ca doriti sa definiti un pointer la membrul toli al clasei. Nu veti avea succes daca încercati astfel:
int * ptoli = &toli; // nu e cod C++ valid
Veti esua deoarece toli nu este de tip int. Deoarece toli este declarata în clasa, are o vizibilitate de clasa. Din acest motiv tipul membrului toli trebuie sa specifice si clasa de care apartine acesta. Pentru ca declaratia sa fie valida, trebuie sa utilizati operatorul de vizibilitate, pentru a identifica clasa pointerului si a membrului:
int exemplu::* ptoli = &exemplu::toli // cod C++ valid
În aceasta declaratie expresia int exemplu::* înseamna tipul "pointer la un int membru al clasei exemplu". Expresia &exemplu::toli înseamna "adresa membrului toli al clasei exemplu
Puteti folosi acest tip de declaratie în functiile membre si în cele prietene. Pointerul ptoli se comporta ca un membru al clasei, adica trebuie apelat cu un obiect al clasei. Aici intervin operatorii pentru dereferentierea membrilor. De exemplu, sa presupunem ca ex este un obiect exemplu declarat într-o functie membra. Pentru a accesa membrul toli al obiectului ex, puteti folosi notatia standard ex.toli. Dar puteti folosi de asemenea si operatorul cu pointerul ptoli
cout << ex.toli; // afiseaza membrul toli
cout << ex.*ptoli; // idem
Deci, operatorul acceseaza un membru folosind numele acestuia, iar acceseaza un membru folosind un pointer spre acesta.
În mod analog, daca pex este un pointer la un obiect exemplu, atunci folositi operatorul -> pentru a accesa membrul toli folosindu-i numele, sau utilizati operatorul de dereferentiere ->* pentru a accesa membrul toli prin intermediul unui pointer la un membru:
pex = &ex; // pex pointer la un obiect exemplu
cout << pex->toli; // afiseaza membrul toli
cout << pex->*ptoli; // idem
Observati ca pex este un pointer la un obiect, în timp ce ptoli este un pointer la un membru al clasei.
Pentru a vedea modul în care functioneaza acesti operatori în practica, îi vom folosi într-o implementare cam întortocheata a functiei operator+(). Functia aduna doua obiecte. Unul este argumentul functiei. Deoarece este un obiect, nu un pointer, puteti folosi operatorul pentru accesul la membrul toli. Celalalt obiect este obiectul apelant, care, va amintiti, este reprezentat de pointerul this. Prin urmare, veti folosi pentru acesta operatorul ->, dupa cum puteti vedea în urmatorul segment de cod:
exemplu exemplu::operator+(exemplu &ex)
Aici ex.*ptoli reprezinta membrul toli al lui ex, iar this->*ptoli reprezinta membrul toli al obiectului pe care îl indica pointerul this. Observati ca ptoli este folosit ca un nume de membru.
Listingul E.1 va prezinta restul definitiilor de metode si o functie main() care utilizeaza clasa.
Listingul E.1 memb_ptoli.cpp
// memb_ptoli.cpp - - dereferentierea pointerilor spre membrii
// clasei
#include <iostream>
using namespace std;
class exemplu
;
exemplu::exemplu()
exemplu::exemplu(int p)
exemplu::~exemplu()
void exemplu::arata_toli()
exemplu exemplu::operator+(exemplu &ex)
int main()
Iata iesirea programului:
180 toli
240 toli
420 toli
|