1.Introducere in C++

2.1. Noi extensii pentru C++

cout, cin si cerr

Īn analogie cu C, C++ defineste driverele standard de intrare si iesire care sīnt deschise cīnd un program este executat. Driverele sīnt:

. cout, analog cu stdout

. cin, analog cu stdin

. cerr, analog cu stderr.

Sintactic, aceste drivere nu sīnt folosite cu functii: datele sīnt citite sau scrise prin intermediul operatorilor << si >> :

#include <iostream.h>

int main()

{

int ival;

char sval[30];

cout << "Introduceti un numar: " << '\n';

cin >> ival;

cout << " Si un sir :" << '\n';

cin >> sval;

cout << " Numarul este :" << ival << "\n iar sirul este :" << sval << '\n\';

return (0);

}

Programul citeste un numar si un sir de pe driverul cin (de obicei tastatura) si afiseaza aceste date pe cout (monitor). Putem face urmatoarele observatii:

. Driverele sīnt declarate īn headerul iostream.h

. Driverele cout, cin si cerr sīnt de fapt 'obiecte' dintr-o anumita clasa care proceseaza intrarea si iesirea unui program.

. Driverul cin citeste datele si copie informatiile īn variabile folosind operatorul >> (deplasare dreapta). Vom descrie mai tīrziu cum pot fi (re)definite actiunile operatorilor īn C++, altfel decīt actiunea lor implicita.

. Operatorii ce manipuleaza cin, cout si cerr (adica >> si << ) manipuleaza īn acelasi timp si variabile de diferite tipuri. Actiunea lor depinde īn final de tipul datelor.

Driverele cin, cout si cerr nu fac parte din gramatica lui C++, asa cum este ea definita de compilatorul ce parseaza fisierul sursa. Definitiile lor apar doar īn fisierul header iostream.h. Īn acelasi timp, se pot folosi si vechile functii printf() si scanf(); e o chestiune de gust. Exista īnsa niste avantaje si dezavantaje:

. Comparat cu functiile standard C, printf () si scanf (), folosirea operatorilor << si >> īmpreuna cu driverele corespunzatoare este mult mai sigura īn ceea ce priveste tipul datelor (formatul lor este descris de programator īn primul caz si este recunoscut de compilator īn cel de-al doilea)

. Functiile printf () si scanf (), precum si alte functii ce folosesc formatul explicit, implementeaza īn fond un mini-limbaj care este interpretat numai la executie. Din contra, compilatorul C++ cunoaste exact ce actiune de intrare/iesire trebuie sa efectueze.

. Folosirea operatorilor << si >> ilustreaza posibilitatile lui C++, īnsa nu īmbunatatesc lizibilitatea programului.

Cuvīntul cheie const

Cuvīntul cheie const apare deseori īn programele C++, desi face parte de asemeni si din limbajul C. El exprima faptul ca valoarea unei variabile sau a unui argument nu poate fi modificata. De exemplu:

int main()

{

int const ival = 3; //o constanta int initializata cu 3

ival = 4 ; // asignarea va duce la un mesaj de eroare

return (0);

}

Variabilele declarate const pot fi, īn opozitie ca īn C, folosite ca specificatori de lungime pentru tablouri, ca īn exemplul:

int const size = 20;

char buf [size]; // tablou de 20 de caractere

O alta utilizare a cuvīntului cheie const apare īn declaratiile pointerilor . Īn declaratia

char const * buf;

buf este o variabila pointer, ce pointeaza catre chars. Tot ce se gaseste la adresa pointata de buf nu poate fi schimbat, deoarece chars a fost declarat constant. Īn schimb, pointerul buf poate fi modificat. Deci atribuirea * buf = 'a' este gresita, īn schimb buf++ nu.

Chiar si declaratia

char const *const buf;

este permisa; aici nici pointerul, nici valoarea de la adresa buf nu pot fi schimbate. Regula de baza este deci: tot ce apare chiar īnainte de cuvīntul cheie const nu poate fi modificat.

Referinte

Īn afara de declaratiile normale de variabile, C++ permite ca referintele sa fie declarate ca sinonime ale variabilelor. O referinta catre o variabila este ca un pseudonim: numele variabilei si numele referintei pot fi folosite īn instructiuni ce afecteaza variabila:

int int_value;

int &ref = int_value;

Īn acest exemplu variabila int_value este definita initial, apoi este definita si referinta ref care conduce la initializarea adresei sale cu adresa variabile int_value. Īn definitia lui ref, operatorul de referinta & indica faptul ca ref nu este el īnsusi un īntreg, ci o referinta la un īntreg. Urmatoarele instructiuni

int_value++;

ref++;

au acelasi efect. La aceeasi locatie de memorie o valoare int este incrementata cu unu; nu conteaza cum este numita locatia.

Referintele joaca un rol important īn C++ ca un mijloc de a pasa argumente ce pot fi modificate (ca argumentele var din Pascal). De exemplu, īn C standard, o functie ce incrementeaza valoarea argumentului sau cu 5 dar nu returneaza nimic (void), necesita un argument pointer:

void increase (int *valp) // asteapta un pointer la un intreg

{ *valp += 5; }

int main()

{

int x;

increase (&x) // adresa lui x este pasata ca argument

return (0);

}

Aceasta constructie poate fi folosita si īn C++, dar acelasi efect poate fi obtinut cu o referinta:

void increase (int &valr) // asteapta o referinta la un īntreg

{ valr += 5 ; }

int main()

{

int x;

increase (x) ; // o referinta la x este pasata ca argument

return (0);

}

Modul īn care compilatorul C++ implementeaza referintele reprezinta de fapt o simpla utilizare de pointeri. Sugestiile ce pot fi date pentru folosirea referintelor ca argumente sīnt urmatoarele:

. Īn acele situatii īn care un apel de functie nu modifica argumentele sale, se poate pasa o copie a variabilelor:

void some_func (int val)

{ printf (" %d \n", val); }

int main()

{ int x;

some_func (x); // este pasata o copie, deci x nu va fi schimbat

return (0);

}

. Īn situatia īn care functia schimba valoarea argumentelor, va fi pasata adresa:

void increase (int * valp)

{ *valp += 5; }

int main()

{ int x ;

increase (&x); // este pasat un pointer, x poate fi schimbat

return (0);

}

. Referintele pot fi rezervate pentru acele cazuri īn care argumentul nu este schimbat de functie, dar unde este preferabil sa folosesti o referinta (pointer de fapt) catre variabila decīt o copie a varibilei. Aceste situatii apar atunci cīnd trebuie pasata o variabila de mari dimensiuni (struct) ; stiva creste foarte mult prin folosirea unei copii a variabilei:

struct person // structura cu dimensiune mare

{ char name [80], address [90];

double salary; };

void printperson (person &p) // functia cere o referinta la o structura

{ printf ("Name : %s\n ","Address : %s\n", p.name, p.address); }

int main()

{

person boss;

printperson (boss); // variabila nu este alterata de functie

}

O īmbunatatire se poate aduce folosind cuvīntul const:

void printperson (person const &p)

. (Mai exista un motiv pentru folosirea referintei pentru pasarea unui obiect ca argument pentru o functie: īn aceste caz activarea unei copii a constructorului obiectului este evitata. Amanunte mai tīrziu).

Referintele pot duce de asemeni la aparitia unui cod 'urīt'. O functie, de exemplu, poate īntoarce o referinta la o variabila, ca īn exemplul urmator:

int &func (void)

{ static int value;

return (value); }

permitīnd urmatoarele constructii:

func () = 20;

func () += func ();

Īn final, evidentiem cīteva diferente īntre pointeri si referinte:

. O referinta nu poate exista prin ea īnsasi, adica fara a face referire la ceva. O declaratie de forma int &ref; nu este permisa (la cine refera ref ?). Exceptii sīnt cele declarate external (se presupune ca ele sīnt initializate īn alta parte), cele cuprinse īn listele de argumente ale unei functii (apelul va face initializarea), referintele ca tip returnat de o functie (functia va determina la ce valoare va face referire) si referintele ce fac parte ca date dintr-o clasa (descrise mai tīrziu). Īn contrast, pointerii sīnt variabile īn sine. Ei pointeaza fie la ceva concret, fie la nimic.

. Cu exceptia cazului referintelor reprezentīnd valoarea returnata de o functie, referintele sīnt pseudonime pentru alte variabile si nu pot fi 'redirectionate" catre alte variabile. Odata referinta definita, ea se refera strict la acea variabila. Din contra, pointerii pot fi oricīnd reasignati.

. Cīnd un operator adresa & este folosit asupra unei referinte, expresia va contine adresa variabilei la care se refera referinta. Din contra, pointerii fiind ei īnsisi variabile, adresa lor nu va avea nici o legatura cu adresa variabilei pointate.

2.2.Functii ca parte dintr-o structura

Am vazut ca functiile pot face parte dintr-o structura. Asemenea functii se numesc functii 'membru'. Īn continuare prezentam modul de definire a unor asemenea functii.

struct person

{ char name [80], address [90] ;

void print (void);

}

Functia membru print () este definita folosind numele structurii (person) si operatorul scope (::):

void person::print ()

{ printf ("Name: %s\n" "Address: %s\n", name, address); }

Īn aceasta definitie a functiei membru, numele functiei este precedat de numele structurii urmat de ::. Observam cum pot fi adresate cīmpurile din structura fara a folosi si numele structurii (variabila name sau address). Deoarece functia print () este parte a structurii person, implicit si variabila name (address) se refera la acelasi tip. Un mod de apel este urmatorul:

person p;

strcpy (p.name, "Karel");

strcpy (p.address, "Copou, nr. 33");

p.print ();

Avantajul unei functii membru consta īn faptul ca apelul functiei poate adresa automat adresele cīmpurilor din structura din care este invocata. De aceea īn instructiunile functiei print () variabilele names si address sīnt cele ale aceleiasi structuri p.

2.3.Ascunderea datelor: public, private

C++ contine posibilitati sintactice speciale pentru implementarea ascunderii datelor (adica a abilitatii programului de a ascunde date de alte parti ale programului, evitīnd adresari gresite sau coliziuni de nume). C++ poseda doua cuvinte cheie speciale legate de acest subiect: private si public. Ele pot fi inserate īn definitia unei structuri. Cuvīntul public defineste toate cīmpurile unei structuri accesibile de īntreg codul program; cuvīntul private defineste toate cīmpurile accesibile doar de codul ce face parte din structura (adica accesibile numai functiilor membru). Mai exista cuvīntul cheie protected. Folosirea lui va fi exemplificata mai tīrziu. Īntr-o structura toate cīmpurile sīnt public daca nu este specificat explicit altceva.

struct person

{

public: void setname(char const *n), setaddress (char const *a), print (void);

char const *getname (void), *getaddress (void);

private: char name [80], address [80];

};

Cīmpurile name si address sīnt singurele accesibile pentru functiile membru definite īn struct, fiind precedate de private. Exemplu:

person x;

x.setname ("Frank"); //o.k., setname() este public

strcpy(x.name, "Kurt"); //gresit, name este privat

Conceptul de ascundere a datelor este realizat astfel īn aceasta structura: datele au nume doar īn interiorul structurii. Ele sīnt accesate din afara de functii speciale, ce fac parte din structura, si care controleaza traficul īntre cīmpurile structurii si restul programului (mai sīnt numite functii 'interfata'). Mai notam ca functiile setname() si setaddress() sīnt declarate ca avīnd un argument char const * (deci nu vor altera sirurile primite ca argument). Īn acelasi timp functiile getname() si getaddress() īntorc un char const *: apelul nu poate modifica sirurile la care vor fi pointate valorile returnate. Iata un exemplu de implementare:

void person::setname (char const * n)

{ strcpy (name, n, 79); name [79] = '\0'; }

char const * person::getname ()

{ return ( (char const * ) name ); }

Īn general, puterea unei functii membru si conceptul de ascundere a datelor sīnt legate de faptul ca functiile interfata pot efectua actiuni specifice, cum ar fi validarea datelor. Un alt exemplu al conceptului de ascundere al datelor este urmatorul: ca o alternativa la functia membru ce pastreaza datele īn memorie (ca mai sus), se poate dezvolta o biblioteca cu functii interfata ce sa stocheze datele pe disc. Conversia programului pentru utilizarea noii metode de stocare se va face doar prin relinkeditarea cu noua biblioteca. Desi ascunderea datelor poate fi realizata prin intermediul struct, cel mai adesea sīnt folosite clasele. O clasa este īn principiu echivalenta cu o structura cu exceptia faptului ca toate cīmpurile sīnt, daca nu e specificat altfel, private. Definitia clasei person se va face īnlocuind cuvīntul struct cu cuvīntul class (si recomandam ca prima litera din numele claselor sa fie litera mare, deci class Person).

2.4.Structuri īn C versus structuri īn C++

Īn C nu este obisnuit sa definesti functii care sa proceseze o structura si care vor necesita ca un argument sa fie un pointer la struct . Iata un fragment de fisier header imaginar īn C:

/* definitia unei structuri PERSON_ */

typedef struct

{

char name [80], address [80];

} PERSON_

/* cīteva functii pentru manipularea structurii PERSON_ */

/* initializeaza cīmpurile cu un nume si o adresa */

extern void initialize (PERSON_ *p, char const *nm, char const * adr);

/* listeaza informatiile */

extern void print (PERSON_ const *p);

/* etc. */

Īn C++, declaratiile functiilor implicate vor fi plasate īn interiorul definitiei structurii (sau clasei). Argumentul ce indica asupra carei structuri se va aplica functia nu mai este deci necesar.

class Person

{

public: void initialize (char const * nm, char const * adr);

void print (void);

// etc...

private: char name [80], address [80];

};

Argumentul struct este implicit īn C++. Un apel de functie īn C

PERSON_ x;

initialize (&x, "un nume", "o adresa");

devine īn C++

Person x;

x.initialize ("un nume", "o adresa");