vuoi
o PayPal
tutte le volte che vuoi
Classi e accesso agli attributi
Oppure:
class nome_classe {
// dichiarazione sezione pubblica
private:
// dichiarazione sezione privata
// Questo è uno dei punti chiave che differenzia le classi dalle strutture: nelle strutture, tutti gli attributi sono pubblici.
// Accesso a puntatori
// Di nuovo allo stesso modo delle strutture, l'accesso ai puntatori avviene attraverso l'operatore ->.
// Es: Punto* p;// inizializzazione del puntatore...
// p->_x = 100;
// p->_y = 200;
// p->riscala(0.5);
// Attributi privati e metodi pubblici
// Classi 3
// Perché gli attributi vengono messi privati?
// Solitamente, nella programmazione a oggetti, due sviluppatori contribuiscono alla realizzazione del programma: il primo definisce le classi, il secondo sviluppa il main.
// Mantenendo gli attributi privati, questi possono essere liberamente modificati dal primo sviluppatore senza che ciò comporti delle modifiche nel main.
// Tipologie di metodi
// Costruttori: sono invocati automaticamente per creare e inizializzare un oggetto.
// Distruttori: sono
}
invocati automaticamente per distruggere un oggetto.
Selettori: accedono agli attributi (privati) di un oggetto e ne restituiscono il valore (il loro nome di solito inizia con get).
Modificatori: accedono agli attributi (privati) di un oggetto e ne modificano il valore (il loro nome di solito inizia con set).
Operatori: elaborano gli attributi di un oggetto e implementano per la classe gli operatori standard C++.
Iteratori: elaborano collezioni di oggetti, ad esempio un array di oggetti.
Costruttori
I costruttori sono metodi invocati automaticamente per la creazione e la inizializzazione di un oggetto.
Come i distruttori, non restituiscono alcun valore di ritorno (nemmeno void).
I costruttori hanno lo stesso nome della classe e possono avere uno o più parametri.
Classi
Una classe, solitamente, ha tre tipi di costruttori:
Costruttore di default: inizializza un oggetto con valori predefiniti (di default) per gli attributi. Ve ne può essere uno soltanto.
Costruttori con parametri:
inizializzano un oggetto con i valori per gli attributi passati come parametri (per riferimento) al costruttore. Una classe può avere più costruttori con parametri.
Costruttore di copia: inizializza un oggetto copiando i valori degli attributi da un altro oggetto passato come parametro (per riferimento costante). Ve ne può essere uno soltanto. Viene chiamato automaticamente quando un oggetto di una classe viene passato per valore.
Es: double distanza (Punto p, Punto q) {
double dx = (p._x - q._x) * (p._x - q._x);
double dy = (p._y - q._y) * (p._y - q._y);
return sqrt (dx + dy);
}
secondo modo: funzione wrapper
double distanza (Punto p, Punto q) {
return p.distanza(q);
}
Dichiarazione di costruttori
Implementazione di costruttori
Punto::Punto() {
_x = 0.0;
_y = 0.0;
}
Punto::Punto (double x,
double y) { Classi 5_x = x; _y = y; } Punto::Punto(const Punto& p) { _x = p._x; _y = p._y; } Nel caso di allocazione dinamica: int main() { Punto p1; //costruttore di default Punto p2(10,20); //costruttore con parametri Punto p4(p2); //costruttore di copia //costruttore di default Punto* pa = new Punto(); //costruttore con parametri Punto* pb = new Punto(10,20); //costruttore di copia Punto* pc = new Punto(*pa); delete pa; delete pb; delete pc; return 0; } Costruttori: classi composte Classi 6 Note 1. Se una classe non ha altri costruttori, il compilatore crea automaticamente un costruttore di default. Tale costruttore, tuttavia, non inizializza gli attributi. Pertanto, è conveniente dichiarare sempre il costruttore di default. 2. Il costruttore di copia viene invocato quando un oggetto è passato per valore ad una funzione. Se la classe non possiede un costruttore di copia viene effettuata la copia bit a bit dell'oggetto. Questo può creare problemi se gli attributi sono, ad esempio,
puntatori a strutture dati complesse
Distruttori
Il distruttore è un metodo speciale di una classe che viene chiamato automaticamente quando si distrugge un oggetto.
Come per i costruttori, ogni classe può avere un solo distruttore, che non ha né tipo né valore di ritorno. Inoltre, un distruttore non ha nemmeno parametri.
Un distruttore viene usato/chiamato quando:
- la variabile corrispondente all'oggetto esce dal proprio campo di visibilità
- l'area di memoria assegnata all'oggetto viene deallocata per mezzo dell'operatore delete
- solitamente, per deallocare la memoria eventualmente allocata dinamicamente nel costruttore
Attenzione: se si dimentica di deallocare la memoria nel distruttore, questa resta allocata e non più utilizzabile
Il distruttore ha lo stesso nome della classe, preceduto dal carattere ~.
Esempio: implementazione e uso
Punto::~Punto() {
cout << "Questo è il distruttore" << endl;
}
int main()
{Punto p1;Punto* p2 = new Punto(10,20);...delete p2; //viene chiamato il distruttore e distrutto p2...return 0; //viene chiamato il distruttore e distrutto p1}
Distruttori: classi composte
Nel caso di classi composte, l'ordine di chiamata dei distruttori è il reciproco rispetto all'ordine di chiamata dei costruttori.
In particolare:
Classi:
Vengono chiamati prima i costruttori delle classi degli oggetti assegnati agli attributi e poi il costruttore della classe composta.
Viene chiamato prima il distruttore della classe composta e poi i distruttori delle classi degli oggetti assegnati agli attributi.
Selettori:
I selettori sono metodi che accedono agli attributi di un oggetto e ne restituiscono il valore. Il loro nome di solito inizia con get.
class Punto {
public:
//...
//selettori
double getX() const;
double getY() const;
private:
double _x;
double _y;
};
double Punto::getX() const {
return _x;
}
double Punto::getY() const {
return _y;
}
int main() {
Punto p1;
Punto p2(25,30);
//stampa le
coordinate di p1cout << p1.getX() << p1.getY() << endl;//stampa le coordinate di p2cout << p2.getX() << p2.getY() << endl;...return 0;}
I selettori sono metodi costanti → nel momento in cui vengono chiamati, non modificano il valore degli attributi. Inoltre, non posso chiamare metodi non costanti.
Per definire metodi costanti, va aggiunta la parola chiave const
sia alla fine della dichiarazione sia alla fine dell'implementazione del metodo.
Nel nostro programma, sono costanti:
void stampa () → void stampa const; → void Punto::stampa() const {...}
double distanza (Punto q) → double distanza (Punto q) const; → double Punto::distanza (Punto q) const;
E' buona pratica mettere costanti tutti i metodi che lo sono.
Modificatori
Classi 8
I modificatori sono metodi che accedono agli attributi e ne modificano il valore (praticamente ciò che noi abbiamo creato come imposta_x e imposta_y)
class Punto {
public:
...// modificatori
void imposta_x(int x) {...}
void imposta_y(int y) {...}
}
<setX(double x);>
<void setY(double y);>
<void setXY(double x, double y);>
<private:double _x;double _y;>;
Dunque:
<void imposta_x (double x) → void setX (double x)>
<void Punto::imposta_x (double x) {_x=x;} → void Punto::setX (double x) {_x=x;}>
Operatori:
Gli operatori sono metodi che elaborano gli attributi di un oggetto.
Essi sono principalmente di due tipi:
Metodi che applicano specifici algoritmi per l’elaborazione degli attributi (ad esempio, per la
classe Punto, il metodo per il calcolo della distanza con un altro punto)
Metodi che implementano per la classe gli operatori standard del linguaggio C++ (ad esempio gli
operatori +, -, =, ++, --, ecc.). In questo caso si parla di sovraccaricamento (overloading) degli
operatori standard.
Operatori algoritmi:
<class Punto {...// operatori>
<void riscala(double f);>
<double distanza(Punto p) const;>
<private:double _x;double _y;>;
<double Punto::distanza(Punto p) const {>
<double dx = (_x - p._x);>
<double dy = (_y - p._y);>
<return sqrt(dx * dx + dy * dy);>
<}>
<int main>
Punto p; // p ha coordinate (0,0)
Punto q(3.0, 4.0);// stampa la distanza tra p e q
cout << p.distanza(q) << endl;
Classi 9// modifica le coordinate di p
p.setXY(2.0, 3.0);// stampa la nuova distanza tra p e q
cout << p.distanza(q) << endl;
return 0;
A volte, per usare gli oggetti secondo un approccio procedurale, si definiscono delle funzioni wrapper.
Es: double distanza(Punto p, Punto q) {return p.distanza(q);}
int main {
Punto p(1.0, 2.0);
Punto q(4.0, 6.0);// stampa la distanza tra p e q
cout << distanza(p, q) << endl;
return 0;
}
Overloading degli operatori
L'overloading serve a implementare gli operatori standard di C++ per le classi, cosa molto utile.
Ciò permette di scrivere, per esempio:
int main {
Punto a(2.0, 3.0);
Punto b(8.0, 5.0);
Punto c = a + b; // questa somma grazie all'overloading...
return 0;
}
Classi 10
Sintassi generica: tipo_ritorno operator_id (lista parametri);
La parola chiave operator indica che si sta effettuando il
sovraccaricamento di un operatore.
N.B.: non si possono cambiare precedenza, raggruppamento e numero degli operandi.
Solitamente, le funzioni operatore sono:
- metodi di una classe
- funzioni non membro, solo se almeno un parametro sia di tipo classe o un riferimento a una classe
Overloading di operatori unari
Sono funzioni operatore, implementate come funzioni membro (ovvero come metodi di una classe), che non hanno parametri, in quanto sovraccaricano operatori unari, che si applicano sull'oggetto stesso per il quale vengono chiamate.
Esempio:
<pre><code>class Punto {
// operatori
Punto operator-();
private:
double _x;
double _y;
};
Punto Punto::operator-() {
return Punto(-getX(), -getY());
}
int main() {
Punto p(3.0, 2.0);
Punto q = -p; // q ha coordinate (-3, -2)
return 0;
}
</code></pre>
Overloading degli operatori ++ e --
Gli operatori unari ++ e -- hanno due forme: la forma prefissa (++p) e quella postfissa (p++).
Entrambe possono essere sovraccaricate:
Forma prefissa:
tipo& operator++(); // o --
Forma postfissa:
tipo operator++(int); // o --
Esempio:
<pre><code>class Numero {
// operatori
Numero& operator++(); // prefisso
Numero operator++(int); // postfisso
private:
int _valore;
};
Numero& Numero::operator++() {
_valore++;
return *this;
}
Numero Numero::operator++(int) {
Numero temp = *this;
_valore++;
return temp;
}
int main() {
Numero n(5);
++n; // prefisso
n++; // postfisso
return 0;
}
</code></pre>
Il parametro int della forma `operator ++(int);` indica che l'operatore di incremento viene applicato al valore dopo che è stato utilizzato nell'espressione corrente. Questo è noto come operatore di post-incremento.