Structuri de date

Structuri de date

O structură de date este o colecție de informațiii grupate sub un singur nume. Aceste informații, cunoscute drept membri, pot avea tipuri de date diferite și lungimi diferite. Structurile de date pot fi definite în C++ folosind următoarea sintaxă:

struct nume_tip {
tip_data_1 id_membru_1;
tip_data_2 id_membru_2;
tip_data_3 id_membru_3;
.
.
} denumiri_obiecte;

unde nume_tip este o denumire pentru tipul structură, denumiri_obiecte poate fi un set de identificatori valizi de obiecte care sunt de acel tip structură. Între acolade {} avem o listă cu informațiile membre, fiecare dintre ele fiind precizată cu tip de dată și un identificator.
De exemplu:

1
2
3
4
5
6
7
struct produs {
  int greutate;
  double pret;
} ;

produs mar;
produs banana, pepene;

Avem o definiție de tip structură, denumit produs, care are doi membri: greutate și pret, fiecare de un alt tip de dată fundamental. Această definiție creează un nou tip (produs), care este apoi folosit pentru a declara trei obiecte (variabile) de acest tip: mar, banana și pepene. Să observăm că, odată definit, tipul produs poate fi folosit la fel ca orice alt tip de dată fundamental.

Chiar la sfârșitul definiției struct, înainte de simbolul (;), câmpul opțional denumiri_obiecte poate fi folosit pentru a declara mai direct obiecte de acel tip structură. De exemplu, obiectele structură mar, banana și pepene pot fi declarate simultan cu definiția tipului de dată structură:

1
2
3
4
struct produs {
  int greutate;
  double pret;
} mar, banana, pepene;

În acest caz, când precizăm denumiri_obiecte, numele tipului de dată (produs) devine opțional: struct necesită fie un nume_tip fie cel puțin o denumire_obiect, dar nu neapărat amândouă simultan.

Este foarte important să înțelegem diferența dintre numele tipului de dată (produd) și un obiect de acest tip (mar, banana și pepene). Putem declara mai multe obiecte (ca mar, banana și pepene) cu un singur tip structură (produs).

O dată declarate cele trei obiecte de un anumit tip (mar, banana și pepene), putem accesa informațiile membru ale lor direct. Sintaxa pentru aceasta presupune inserarea simbolului punct (.) între denumirea obiectului și identificatorul membrului. De exemplu, am putea opera cu oricare dintre aceste elemente ca și cum ele ar fi variabile obișnuite declarate cu propriul lor tip de dată:

1
2
3
4
5
6
mar.greutate
mar.pret
banana.greutate
banana.pret
pepene.greutate
pepene.

Fiecare dintre ele are tipul de dată corespunzător membrului la care se referă: mar.greutate, banana.greutatet și pepene.greutate sunt de tip int, în timp ce mar.pret, banana.pret și pepene.pret sunt de tip double.

Avem aici un exemplu real de folosire a tipului structură:

// exemplu cu structuri
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

struct filme_t {
  string titlu;
  int an;
} al_meu, al_tau;

void afis_film (filme_t film);

int main ()
{
  string mystr;

  al_meu.titlu = "2001 A Space Odyssey";
  al_meu.an = 1968;

  cout << "Tasteaza titlul: ";
  getline (cin,al_tau.titlu);
  cout << "Tasteaza anul: ";
  getline (cin,mystr);
  stringstream(mystr) >> al_tau.an;

  cout << "Filmul meu favorit este:\n ";
  afis_film (al_meu);
  cout << "Si al tau este:\n ";
  afis_film (al_tau);
  return 0;
}

void afis_film (filme_t film)
{
  cout << film.titlu;
  cout << " (" << film.an << ")\n";
}
Tasteaza titlul: Alien
Tasteaza anul: 1979

Filmul meu favorit este:
 2001 A Space Odyssey (1968)
Si al tau este:
 Alien (1979)

Exemplul arată cum acționează datele membru exact ca variabilele obișnuite. De exemplu, membrul al_tau.an este o variabilă validă de tip int, iar al_meu.titlu este o variabilă validă de tip string.

Dar și obiectele al_meu și al_tau sunt de același tip (de tipul filme_t). De exemplu, ambele au fost transmise ca parametru funcției afis_film ca și cum ar fi fost variabile simple. De aceea, unul dintre avantajele structurilor de date este abilitatea de a ne referi atăt la informațiile membru individual, căt și la întreaga entitate formată de colecția (structura) de date. În ambele cazuri folosim același identificator: numele structurii.

Deoarece structurile sunt tipuri de date, ele pot fi folosite ca tip de bază pentru tablouri atunci când construim tabele sau baze de date cu ele:

// tablou de structuri
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

struct filme_t {
  string titlu;
  int an;
} filme [3];

void afis_film (filme_t film);

int main ()
{
  string mystr;
  int n;

  for (n=0; n<3; n++)
  {
    cout << "Tasteaza titlul: ";
    getline (cin,filme[n].titlu);
    cout << "Tasteaza anul: ";
    getline (cin,mystr);
    stringstream(mystr) >> filme[n].an;
  }

  cout << "\nAi introdus urmatoarele filme:\n";
  for (n=0; n<3; n++)
    afis_film (filme[n]);
  return 0;
}

void afis_film (filme_t film)
{
  cout << film.titlu;
  cout << " (" << film.an << ")\n";
}
Tasteaza titlul: Blade Runner
Tasteaza anul: 1982
Tasteaza titlul: The Matrix
Tasteaza anul: 1999
Tasteaza titlul: Taxi Driver
Tasteaza anul: 1976
 
Ai introdus urmatoarele filme:
Blade Runner (1982)
The Matrix (1999)
Taxi Driver (1976)

Pointeri către structuri

Ca orice alt tip, structurile pot fi pointate de către propriul tip de pointeri:

1
2
3
4
5
6
7
struct filme_t {
  string titlu;
  int an;
};

filme_t un_film;
filme_t * pfilm;

Aici un_film este un obiect de tipul structură filme_t, iar pfilm este un pointer care pointează către obiecte de tipul filme_t. De aceea, următoarea secvență de cod este validă:

1
pfilm = &un_film;

Valoarea pointerului pfilm se obține prin atribuirea adresei obiectului un_film.

Acum, să vedem un alt exemplu care mixează pointeri și structuri și ne va servi pentru introducerea unui nou operator: operatorul săgeată (->):

// pointeri către structuri
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

struct filme_t {
  string titlu;
  int an;
};

int main ()
{
  string mystr;

  filme_t un_film;
  filme_t * pfilm;
  pfilm = &un_film;

  cout << "Tasteaza titlul: ";
  getline (cin, pfilm->titlu);
  cout << "Tasteaza anul: ";
  getline (cin, mystr);
  (stringstream) mystr >> pfilm->an;

  cout << "\nAi tastat:\n";
  cout << pfilm->titlu;
  cout << " (" << pfilm->an << ")\n";

  return 0;
}
Tasteaza titlul: Invasion of the body snatchers
Tasteaza anul: 1978
 
Ai tastat:
Invasion of the body snatchers (1978)

Operatorul săgeată (->) este un operator de dereferențiere folosit numai cu pointer către obiecte care au membri. Acest operator servește pentru a accesa membrii unui obiect direct prin adresa lor. De exemplu, în programul de mai sus:

1
pfilm->

este echivalent, întotdeauna, cu:

1
(*pfilm).

Ambele expresii, pmovie->titlu și (*pfilm).titlu sunt valide și accesează membrul titlu al structurii pointate de un pointer numit pfilm. Evident, sunt diferite de:

1
*pfilm.

care este echivalentă cu:

1
*(pfilm.titlu)

Prin acestea se accesează valoarea pointată de un membru pointer ipotetic, denumit titlu, al obiectului structură pfilm (dar aici nu este cazul, căci titlu nu este de tip pointer). Următorul tabel sintetizează posibile combinații de operatori pentru pointeri și pentru membrii unei structuri:

ExpresieCe se evalueazaExpresie echivalentă
a.bMembrul b al obiectului a
a->bMembrul b al obiectului pointat de a(*a).b
*a.bValoarea pointată de membrul b al obiectului a*(a.b)

Structuri imbricate

Structurile pot fi imbricate astfel încât un element al unei structuri să fie el însuși o altă structură:

1
2
3
4
5
6
7
8
9
10
11
12
struct filme_t {
  string titlu;
  int an;
};

struct prieteni_t {
  string nume;
  string email;
  filme_t film_favorit;
} charlie, maria;

friends_t * pprieteni = &charlie;

După declarația anterioară, sunt valide toate expresiile următoare:

1
2
3
4
charlie.nume
maria.film_favorit.titlu
charlie.film_favorit.an
pprieteni->film_favorit.

(unde, de fapt, ultimele două expresii se referă la același membru).
Index
Index