edutecnica

Classi

     

Una classe è un tipo aggegato che oltre ai dati definisce anche le funzioni che possono manipolarli.

La dichiarazione è simile a quella di una struttura:

class Punto {
public:
  . . .
  double getX();
  double getY();
private:
  double x;
  double y;
};

I dati membri e le funzioni membro che seguono lo specificatore d'accesso public sono accessibili direttamente.
L'accesso a quelli dichiarati dopo private, invece, è riservato solo alle funzioni della classe.

Dichiarazione di oggetti

     

Un oggetto è un'istanza di una classe ovvero una variabile di quel tipo:

Punto p; // p è un oggetto

L'accesso ai membri degli oggetti avviene, come per le strutture, mediante l'operatore punto . o l'operatore freccia -> (nel caso di puntatori a oggetti):

// Ok! getX e getY sono public
cout << p.getX()<< ","<< p.getY();

Non è possibile, invece, accedere ai membri privati:

// ERRORE! x e y sono privati
cout << p.x << ',' << p.y;

Dichiarazione dinamica di oggetti

     

Gli oggetti dichiarati all'interno delle funzioni sono allocati nell'area di stack e il loro periodo di vita e visibilità è soggetto alle stesse regole delle variabili locali.
Nello stack, inoltre, non è possibile creare strutture dati dinamiche.
Se tali limitazioni sono troppo restrittive, gli oggetti possono essere dichiarati nel segmento di heap mediante l'operatore new.

esempio:

Punto *p, *vett;
p = new Punto;
vett = new Punto[dim];

In questo esempio p e vett sono dichiarati come puntatori a oggetti di classe Punto.
p punta a un singolo punto mentre vett rappresenta un vettore di dim punti.
Entrambi sono allocati nell'area di heap.
Poiché p e vett sono puntatori, per accedere ai membri dell'oggetto è necessario impiegare l'operatore.freccia ->:

cout << p->getX() << endl;
cout << vett[i]->getY() << endl;

Definizione di member function

     

Le funzioni membro (member function) possono essere definite nella dichiaraziore della classe cui appartengono oppure all'esterno.
Nel primo caso la definizione equivale a quelle delle funzioni inline, senza che occorra specificare questa keyword :

class Punto {
public:
  . . .
  // Dichiarazione inline
  double getX(){return 0;}
  . . .
}

Se la definizione è esterna, per indicare che la funzione è membro di quella classe il suo nome va fatto precedere da quello della classe e dall'operatore di scope resolution.

double Punto::getY(){
  return y;
}

Costruttori

     

Prima di impiegare un oggetto è sempre bene inizializzarlo.
Questo compito può essere svolto automaticamente, anche con modalità differenti, da member function speciali: i costruttori, che vengono sempre chiamati quando si dichiara un oggetto.
Se nella classe non ne è presente nessuno, viene chiamato il costruttore di default.

class Punto {
public:
  // Costruttori
  Punto(){x=0; y=0;}
  Punto(double a, double b) {x = a; y = b;}
  . . .
};

int main(){
  // È chiamato il 1° costruttore
  Punto p1;
  // È chiamato il 2° costruttore
  Punto p2(1.5, -3.9);
  . . .
}

Un costruttore non ha valori di ritorno.

Distruttori

     

Se l'oggetto alloca della memoria dinamicamente o apre dei file, prima di venire distrutto è necessario che compia delle azioni appropriate per restituire tali risorse al sistema operativo.
Queste operazioni possono essere svolte da una speciale member function, denominata distruttore.
Il distruttore è automaticamente chiamato al termine del periodo di vita dell'oggetto.
Ogni oggetto può possedere un solo distruttore; se nonè definito sarà utilizzato il distruttore di default.
Un distruttore ha lo stesso nome della classe preceduto dal segno tilde ~ e non ha parametri né valore di ritorno.

class Punto {
public:
  . . .
  // Distruttore
  ~Punto();
  . . .
}

Costruttore copia

     

Quando si inizializza un oggetto con un altro, per impostazione predefinita avviene una copia membro a membro dei dati.
Se questo non è il comportamento desiderato, è possibile modificarlo definendo una speciale member function denominata costruttore copia.
Un costruttore copia è un costruttore che, come unico parametro, possiede un riferimento (reference) a un oggetto della stessa classe.
Se definito, sarà richiamato al momento dell'inizializzazione di un oggetto.

class Stringa {
public:
  // Costruttore copia
  Stringa(Stringa &str) ;

  . . .
}
. . .
Stringa s = "Alfa Beta Gamma"
/* Per inizializzare t è chiamato il costruttore copia */
Stringa t = s;

Overloading degli operatori

     

Spesso capita di compiere operazioni su degli oggetti la cui logica richiama quella degli operatori tradizionali, per esempio quando si assegna un oggetto a un altro o quando si sommano due oggetti di tipo numerico.
In C++ è possibile estendere il significato degli operatori standard adattandolo alle esigenze di una classe di oggetti. Questo meccanismo prende il nome di overloading degli operatori.

esempio:

class Complesso {
public:
  Complesso operator+(Complesso z) ;
  . . .
}

Nell'esempio è stata dichiarata una speciale member function, operatori, seguita dal simbolo dell'operatore da ridefinire, in questo caso quello di somma. Quando tra due oggetti di questa classe viene incontrato il segno +, sarà automaticamente eseguito il codice contenuto nel corpo della corrispondente member function operator():

Complesso x(-1, 5), y(2,4), z;
z=x+y; // operator+

Il puntatore this

     

Per determinare su quale oggetto deve operare una member function di una classe, al momento della chiamata le viene implicitamente passato un suo puntatore.
Se occorre, all'interno delle member function è possibile riferirsi a quel puntatore utilizzando l'identificatore this.
esempio:

T &T::operator=(const T &var){
  // var è uguale a this?
  if (&var == this) return *this;
}

In questo esempio il puntatore this è stato impiegato per veriflcare se nella member function operator= della classe T l'argomento a destra dell'operatore = (var) coincide con quello di sinistra. In caso affermativo la funzione ritoma un reference all'oggetto di sinistra.

Funzioni e classi friend

     

In alcuni casi può essere necessario accedere dall'esterno ai membri privati di un classe. Questa possibilità, normalmente vietata, può essere concessa eccezionalmente a funzioni esterne se sono dichiarate all'interno della classe precedute dallo specificatore friend.

esempio:

class T {
public:
  T operator=(const T &x);
  friend T operator+(double x, T z);
  . . .
};
. . .
T operator+(double x, T z) {
  . . .
}

Nell'esempio, la funzione operator+ dichiarata nella classe non è una sua member function di T, ma una funzione friend, ovvero una funzione esterna che ha il privilegio di potere accedere ai membri privati di T. Lo specificatore friend va usato solo all'interno della classe.
All'interno di una classe è possibile specificare anche classi friend. In questo caso le member function della classe friend avranno gli stessi privilegi delle proprie member function.

Membri static

     

All'interno di una classe è possibile dichiarare data member i cui valori sono comuni a tutti gli oggetti di quella classe. La dichiarazione è analoga a quella degli altri data member, ma è preceduta dallo specificatore static.

Esempio:

class T {
public:
  . . .
private:
  static unsigned Contatore;
  . . .
};

L'inizializzazione di Contatore non avviene per opera del costruttore di T, ma esternamente alla classe, come per una variabile globale:

unsigned T::Contatore = 0;

Anche le member function possono essere static, nel quale caso potranno accedere solo a membri static.

Oggetti e membri const

     

Gli oggetti possono essere dichiarati costanti mediante la keyword const. In questo caso non sarà possibile modificare i loro data member né applicare metodi che non siano dichiarati const.
Per dichiarare una member function come const occorre scrivere questa parola chiave in fondo all'intestazione della sua dichiarazione e della sua definizione.

Esempio:

class T {
public:
  int Get() const;
  void Set(int j);
  . . .
};
. . .
const T obj;
. . .
obj.Set(5); // ERRORE! obj è const
cout << obj.Get(); // OK! const
. . .
int T::Get() const{
  . . .
}