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{
. . .
}