next up previous contents index
Next: Puntatori a strutture Up: Memoria dinamica. Strutture. Liste Previous: Array dinamici   Indice   Indice analitico

Strutture

In questa sezione vedremo come costruire nuovi tipi aggregati: le strutture; essi, a differenza dell'array, possono essere costituiti da oggetti di diverso tipo. Eviteremo volutamente di esaminare a fondo i dettagli riguardanti le strutture, in quanto tale costruttore di tipi è formalmente simile al costruttore classe, di cui si discuterà ampiamente nella parte finale di questo testo. Abbiamo detto e ripetuto che un linguaggio di programmazione fornisce delle astrazioni al programmatore, il quale può dunque modellizare i problemi con cui ha a che fare in maniera semplice; per esempio, è semplice ed intuitivo modellizzare una votazione scolastica con un int, o il nome di una persona con una stringa, o una matrice algebrica con un array bidimensionale di double. Come possiamo però gestire oggetti più complessi, come un rettangolo, o le coordinate di una persona? Non possiamo se sfruttiamo semplicemente i tipi primitivi; infatti, avremmo bisogno, rispettivamente, di due interi per un rettangolo, e alcune variabili di tipi diversi per i dati personali. Possiamo allora creare una struttura: un nuovo tipo contenente un arbitrario numero di variabili (o strutture) di tipi diversi. Un esempio è il seguente:

struct Rettangolo {
  double lunghezza;
  double altezza;
};

La sintassi per la creazione di una struttura è evidente: dopo la parola chiave struct basta inserire il nome della struttura e, tra parentesi graffe ({ }), l'elenco dei campi che costituiscono la struttura; si noti che la parentesi che chiude la struttura è seguita da un punto e virgola. Nell'esempio che abbiamo appena presentato abbiamo dunque creato un nuovo tipo, chiamato Rettangolo, che possiamo utilizzare esattamente come se fosse un tipo primitivo; è possibile infatti dichiarare puntatori, array, riferimenti a Rettangolo. Per convenzione stilistica, i nomi delle strutture sono costituiti da lettere interamente minuscole, tranne la prima.

Dichiarata una variabile di un nuovo tipo derivato tramite struct, è possibile accedere ai suoi campi tramite un nuovo operatore: il punto (`.'), come nel seguente esempio:


// ex7_4_1
#include <iostream.h>

struct Rettangolo {
  double larghezza;
  double altezza;
};

void main() {
  Rettangolo r;
  cout << "larghezza? "; cin >> r.larghezza;
  cout << "altezza? "; cin >> r.altezza;
  double area = r.larghezza * r.altezza;
  cout << "area = " << area;
}

esempio di output:
larghezza? 7
altezza? 4
area = 28

Come è ovvio, avremmo potuto utilizzare due variabili reali, piuttosto che una struttura Rettangolo, ottenendo il medesimo risultato; tuttavia l'astrazione ottenuta è maggiore utilizzando questo composto, in quanto possiamo creare dei veri e propri ``rettangoli'', identificati da una altezza e una larghezza, riducendo enormemente lo sforzo programmativo e la possibilità di commettere errori. Vediamo il seguente


// ex7_4_2
#include <iostream.h>

struct Rettangolo {
  double larghezza;
  double altezza;
};

// funzione che immette nRettangoli nel vettore v
void immetti (Rettangolo* v, int nRettangoli) {
  for (int i = 0; i < nRettangoli; i++) {
    cout << "rettangolo " << i+1 << "? ";
    cin >> v[i].larghezza >> v[i].altezza;
  }
}

// funzione che torna il rettangolo avente area minore
// tra quelli contenuti nel vettore v
Rettangolo minore (Rettangolo* v, int nRettangoli) {
  Rettangolo min = v[0];
  for (int i = 1; i < nRettangoli; i++) {
    double areaCorrente = v[i].larghezza * v[i].altezza;
    double areaMin = min.larghezza * min.altezza;
    if (areaCorrente < areaMin)
      min = v[i];
  }
  return min;
}

void main() {
  int nRettangoli;
  cout << "quanti rettangoli? "; cin >> nRettangoli;
  // creiamo un array dinamico di rettangoli
  Rettangolo* v = new Rettangolo[nRettangoli];
  immetti (v, nRettangoli);
  Rettangolo m = minore (v, nRettangoli);
  cout << "il rettangolo avente area minore e`: " <<
    m.larghezza << " x " << m.altezza << "\n";
}

esempio di output:
quanti rettangoli? 3
rettangolo 1? 3 5
rettangolo 2? 4 10
rettangolo 3? 2 7
il rettangolo avente area minore e`: 2 x 7

Come si vede nel seguente esempio, l'utilizzo di variabili di tipo Rettangolo è esattamente analogo a quello di variabili dei tipi primitivi; è possibile inoltre costruire strutture contenenti altre strutture, nel qual caso l'accesso ai campi della struttura più interna si effettua tramite la concatenazione di operatori punto:


// ex7_4_3
#include <iostream.h>

struct Rettangolo {
  double larghezza;
  double altezza;
};

struct Rombo {
  double diagonale1;
  double diagonale2;
};

struct Triangolo {
  double base;
  double altezza;
};

struct Composizione {
  Rettangolo rettangolo;
  Rombo rombo;
  Triangolo triangolo;
};

void main() {
  Composizione c;
  cout << "Rettangolo\n";
  cout << "larghezza? "; cin >> c.rettangolo.larghezza;
  cout << "altezza? "; cin >> c.rettangolo.altezza;
  cout << "Rombo\n";
  cout << "diagonale 1? "; cin >> c.rombo.diagonale1;
  cout << "diagonale 2? "; cin >> c.rombo.diagonale2;
  cout << "Triangolo\n";
  cout << "base? "; cin >> c.triangolo.base;
  cout << "altezza? "; cin >> c.triangolo.altezza;
  cout << "le superfici sono, rispettivamente:\n" <<
    c.rettangolo.larghezza * c.rettangolo.altezza << ", " <<
    c.rombo.diagonale1 * c.rombo.diagonale2 / 2 << ", " <<
    c.triangolo.base * c.triangolo.altezza / 2 << "\n";
}

esempio di output:
Rettangolo
larghezza? 5.3
altezza? 3.2
Rombo
diagonale 1? 7
diagonale 2? 6.4
Triangolo
base? 10
altezza? 4.5
le superfici sono, rispettivamente:
16.96, 22.4, 22.5

Vediamo ora come sia possibile utilizzare una struttura per contenere dati eterogenei, come i dati personali:


// ex7_4_4
#include <iostream.h>
#include <string.h>

enum Stabilimento { ROMA, MILANO, TORINO };

enum Qualifica { OPERAIO, TECNICO, CAPO_REPARTO,
                 CAPO_SEZIONE, DIRETTORE, SOCIO };

struct Impiegato {
  char nome[20];
  char cognome[30];
  Qualifica qualifica;
  Stabilimento stabilimento;
  int salario;
};

void stampaDati (const Impiegato& i) {
  char q[20], s[10];
  switch (i.qualifica) {
  case OPERAIO:      strcpy (q, "operario");     break;
  case TECNICO:      strcpy (q, "tecnico");      break;
  case CAPO_REPARTO: strcpy (q, "capo reparto"); break;
  case CAPO_SEZIONE: strcpy (q, "capo sezione"); break;
  case DIRETTORE:    strcpy (q, "direttore");    break;
  case SOCIO:        strcpy (q, "socio");        break;
  }
  switch (i.stabilimento) {
  case ROMA:   strcpy (s, "Roma");   break;
  case MILANO: strcpy (s, "Milano"); break;
  case TORINO: strcpy (s, "Torino"); break;
  }
  cout << "\n" << i.nome << " " << i.cognome <<
    "\n" << q << " presso lo stabilimento di " << s;
  if (i.qualifica != SOCIO)
    cout << "\nsalario di lire " << i.salario;
}

void main() {
  int q, s;
  Impiegato i;
  cout << "Dati di un impiegato\n";
  cout << "nome? "; cin >> i.nome;
  cout << "cognome? "; cin >> i.cognome;
  cout << "Qualifica\n"
    "\t(0) operaio\n"
    "\t(1) tecnico\n"
    "\t(2) capo reparto\n"
    "\t(3) capo sezione\n"
    "\t(4) direttore\n"
    "\t(5) socio\n? "; cin >> q;
  switch (q) {
  case 0: i.qualifica = OPERAIO;      break;
  case 1: i.qualifica = TECNICO;      break;
  case 2: i.qualifica = CAPO_REPARTO; break;
  case 3: i.qualifica = CAPO_SEZIONE; break;
  case 4: i.qualifica = DIRETTORE;    break;
  case 5: i.qualifica = SOCIO;        break;
  }
  cout << "Stabilimento\n"
    "\t(0) Roma\n"
    "\t(1) Milano\n"
    "\t(2) Torino\n? "; cin >> s;
  switch (s) {
  case 0: i.stabilimento = ROMA; break;
  case 1: i.stabilimento = MILANO; break;
  case 2: i.stabilimento = TORINO; break;
  }
  // se si tratta di un socio il salario NON e` definito
  if (i.qualifica != SOCIO) {
    cout << "salario? ";
    cin >> i.salario;
  }
  stampaDati (i);
}

esempio di output:
Dati di un impiegato
nome? Paolo
cognome? Rossi
Qualifica
(0) operaio
(1) tecnico
(2) capo reparto
(3) capo sezione
(4) direttore
(5) socio
? 2
Stabilimento
(0) Roma
(1) Milano
(2) Torino
? 0
salario? 3500000

Paolo Rossi
capo reparto presso lo stabilimento di Roma
salario di lire 3500000

Notiamo che il passaggio della struttura Impiegato avviene per riferimento costante; quando si passa un argomento ad una funzione, se tale argomento è un oggetto più complesso di un semplice tipo primitivo, conviene sempre passare l'argomento per riferimento: non costante se l'oggetto va modificato, costante in caso contrario (come nel nostro esempio). Il passaggio per valore, infatti, avviene copiando membro a membro i campi dalla struttura locale alla funzione chiamante in quella della struttura chiamata: tale operazione potrebbe essere del tutto inefficiente nel caso l'oggetto occupi un gran quantitativo di memoria.

Una struttura in definitiva permette di avere a disposizione dei dati astratti, il cui utilizzo è immediato ed intuitivo; per programmi semplici (come gli esempi da noi presentati, per intenderci) una raffinatezza del genere non è necessaria, in quanto è facile tenere a mente la struttura dell'intero programma senza perdere di vista il modello reale. Al contrario, per un programma ``vero'' diventa assolutamente necessario costruire dei tipi ad hoc; comunque le strutture non sono che il primo passo verso la programmazione ad oggetti, che prenderà il volo solo con la presentazione delle classi.

ex-2
si scriva una struttura che identifichi un pixel dello schermo: ascissa, ordinata, colore; si supponga lo schermo avente risoluzione 800 $\times$ 600, per il colore si utilizzi un intero compreso tra 0 (nero) e 16777216 (bianco);
ex-3
si scriva una struttura contenente i seguenti dati di un pacco postale: ufficio di provenienza, ufficio di destinazione, peso, dimensioni (altezza, larghezza, profondità), costo della spedizione;
ex-4
si scriva una funzione che accetti un Rettangolo e raddoppi le sue dimensioni; il passaggio deve avvenire per riferimento? Perché?
ex-5
si scriva una funzione che accetti un Rombo e torni il suo perimetro; il passaggio deve avvenire per riferimento? Perché?


next up previous contents index
Next: Puntatori a strutture Up: Memoria dinamica. Strutture. Liste Previous: Array dinamici   Indice   Indice analitico
Claudio Cicconetti
2000-09-06