ITA - Appunti Cpp 013 - Programmazione ad Oggetti
September 2024 (2276 Words, 13 Minutes)
La programmazione ad oggetti (OOP) è un paradigma di programmazione che utilizza oggetti e classi per organizzare e gestire il codice. In C++, l’OOP è una componente fondamentale che consente di creare applicazioni modulari, riutilizzabili e manutenibili.
Classi
Differenza tra Oggetti e Classi
- Classe: È un modello che definisce le proprietà e i comportamenti comuni a tutti gli oggetti di quel tipo. Una classe specifica i dati (membri di dati) e le funzioni (metodi) che possono operare su quei dati.
- Oggetto: È un’istanza di una classe. Quando una classe viene definita, nessuna memoria viene allocata fino a quando non viene creata un’istanza della classe (cioè un oggetto).
Dichiarazione di una Classe e Istanziazione di un Oggetto
Il C++ moderno ha introdotto la possibilità di chiamare i costruttori usando l’inizializzazione uniforme
usando le parentesi graffe {}
invece delle parentesi tonde ()
:
// classi e inizializzazione uniforme
#include <iostream>
using namespace std;
class Cerchio {
double raggio;
public:
Cerchio(double r) { raggio = r; }
double circonferenza() {return 2*raggio*3.14159265;}
};
int main () {
Cerchio foo (10.0); // forma funzionale
Cerchio bar = 20.0; // inizializzazione per assegnamento.
Cerchio baz {30.0}; // inizializzazione uniforme.
Cerchio qux = {40.0}; // POD-like
return 0;
}
Un vantaggio dell’inizializzazione uniforme rispetto alla forma funzionale è che, a differenza delle parentesi tonde, le parentesi graffe non possono essere confuse con le dichiarazioni di funzione e quindi possono essere usate per chiamare esplicitamente i costruttori predefiniti.
Modificatori di Accesso: Public, Private e Protected
- Public: Membri dichiarati public sono accessibili da qualsiasi parte del programma.
- Private: Membri dichiarati private sono accessibili solo all’interno della classe stessa.
- Protected: Membri dichiarati protected sono accessibili all’interno della classe stessa e dalle classi derivate (sottoclassi).
class Esempio {
public:
int pubblico;
private:
int privato;
protected:
int protetto;
};
Definizione di un Metodo
I metodi di una classe possono essere definiti all’interno della dichiarazione della classe o all’esterno utilizzando l’operatore di risoluzione dello scope ::
.
class Rettangolo {
public:
int larghezza, altezza;
// Definizione all'interno della classe
int area() {
return larghezza * altezza;
}
// Dichiarazione del metodo
void setDimensioni(int l, int a);
};
// Definizione del metodo all'esterno della classe
void Rettangolo::setDimensioni(int l, int a) {
larghezza = l;
altezza = a;
}
Membri di Classe
I membri di classe sono variabili o costanti definite all’interno di una classe. Possono essere di due tipi: membri di istanza e membri statici.
class Contatore {
public:
static int conteggio; // Membro statico
Contatore() {
conteggio++;
}
};
// Inizializzazione del membro statico
int Contatore::conteggio = 0;
Costruttore e Distruttore
- Costruttore: È una funzione speciale che viene chiamata automaticamente quando un oggetto della classe viene creato. Ha lo stesso nome della classe e non ha un tipo di ritorno.
- Distruttore: È una funzione speciale che viene chiamata automaticamente quando un oggetto della classe viene distrutto. Ha lo stesso nome della classe preceduto da un tilde
~
.
class Esempio {
public:
Esempio() {
std::cout << "Costruttore chiamato." << std::endl;
}
~Esempio() {
std::cout << "Distruttore chiamato." << std::endl;
}
};
Costruttore di Default e Distruttore di Default
- Costruttore di Default: Se non viene definito alcun costruttore, il compilatore fornisce automaticamente un costruttore di default.
- Distruttore di Default: Se non viene definito alcun distruttore, il compilatore fornisce automaticamente un distruttore di default.
Inizializzazione e Parametri di Default
I costruttori possono avere parametri di default che permettono di creare oggetti con valori predefiniti.
class Punto {
public:
int x, y;
Punto(int x = 0, int y = 0) : x(x), y(y) {} // Parametri di default
};
int main() {
Punto p1; // x = 0, y = 0
Punto p2(5); // x = 5, y = 0
Punto p3(3, 4); // x = 3, y = 4
return 0;
}
Copy Constructor
Il copy constructor è un costruttore che inizializza un oggetto utilizzando un altro oggetto dello stesso tipo.
class Copia {
public:
int* dato;
// Costruttore
Copia(int val) {
dato = new int(val);
}
// Copy constructor
Copia(const Copia& altro) {
dato = new int(*altro.dato);
}
~Copia() {
delete dato;
}
};
Move Constructor
Il move constructor è un costruttore che trasferisce le risorse da un oggetto a un altro senza effettuare una copia profonda, migliorando le prestazioni.
class Sposta {
public:
int* dato;
Sposta(int val) {
dato = new int(val);
}
// Move constructor
Sposta(Sposta&& altro) noexcept : dato(altro.dato) {
altro.dato = nullptr;
}
~Sposta() {
delete dato;
}
};
Delega del Costruttore
La delega del costruttore è una funzionalità introdotta in C++11 che consente a un costruttore di una classe di chiamare un altro costruttore della stessa classe. Questo può essere utile per evitare la duplicazione del codice e per centralizzare l’inizializzazione degli oggetti.
Supponiamo di avere una classe Rettangolo
con più costruttori. Possiamo utilizzare la delega del costruttore per centralizzare la logica di inizializzazione.
class Rettangolo {
public:
int larghezza;
int altezza;
// Costruttore delegato
Rettangolo() : Rettangolo(0, 0) {}
// Costruttore principale
Rettangolo(int l, int a) : larghezza(l), altezza(a) {}
int area() {
return larghezza * altezza;
}
};
int main() {
Rettangolo r1; // Utilizza il costruttore delegato
Rettangolo r2(5, 10); // Utilizza il costruttore principale
std::cout << "Area r1: " << r1.area() << std::endl; // Output: Area r1: 0
std::cout << "Area r2: " << r2.area() << std::endl; // Output: Area r2: 50
return 0;
}
In questo esempio, il costruttore senza parametri delega la chiamata al costruttore principale con due parametri. Questo approccio semplifica la gestione dei costruttori e garantisce che tutta l’inizializzazione venga eseguita in un unico punto.
Puntatori
Il Puntatore this
Il puntatore this
è un puntatore implicito che si riferisce all’oggetto corrente. È utile per accedere ai membri dell’oggetto corrente e per risolvere ambiguità tra i membri della classe e i parametri con lo stesso nome.
class Punto {
public:
int x, y;
void setPunti(int x, int y) {
this->x = x; // `this->x` si riferisce al membro della classe
this->y = y; // `y` è il parametro della funzione
}
};
Puntatori a Oggetto
Gli oggetti possono anche essere puntati da puntatori: una volta dichiarata, una classe diventa un tipo valido, quindi può essere utilizzata come il tipo puntato da un puntatore:
Rettangolo * prect;
questo è puntatore a un oggetto della classe Rettangolo.
Analogamente alle strutture dati semplici, è possibile accedere ai membri di un oggetto direttamente da un puntatore utilizzando l’operatore freccia (->). Ecco un esempio con alcune possibili combinazioni:
// esempio di puntatore a oggetto
#include <iostream>
using namespace std;
class Rettangolo {
int larghezza, altezza;
public:
Rettangolo(int x, int y) : larghezza(x), altezza(y) {}
int area(void) { return larghezza * altezza; }
};
int main() {
Rettangolo obj (3, 4);
Rettangolo * foo, * bar, * baz;
foo = &obj;
bar = new Rettangolo (5, 6);
baz = new Rettangolo[2] { {2,5}, {3,6} };
cout << "area di obj: " << obj.area() << '\n';
cout << "area di *foo: " << foo->area() << '\n';
cout << "area di *bar: " << bar->area() << '\n';
cout << "area di baz[0]:" << baz[0].area() << '\n';
cout << "area di baz[1]:" << baz[1].area() << '\n';
delete bar;
delete[] baz;
return 0;
}
Questo esempio utilizza diversi operatori per operare su oggetti e puntatori (operatori *
, &
, .
, ->
, []
). Possono essere interpretati come:
espressione | interpretazione |
---|---|
*x |
puntato da x |
&x |
indirizzo di x |
x.y |
membro y dell’oggetto x |
x->y |
membro y dell’oggetto puntato da x |
(*x).y |
membro y dell’oggetto puntato da x (equivalente al precedente) |
x[0] |
primo oggetto puntato da x |
x[1] |
secondo oggetto puntato da x |
x[n] |
(n+1)esimo oggetto puntato da x |
Oggetti e Costanti
Classi Const
Le classi const non permettono di modificare i membri dell’oggetto dopo la sua creazione.
class Costante {
public:
int valore;
Costante(int v) : valore(v) {}
void stampa() const {
std::cout << valore << std::endl;
}
};
int main() {
const Costante c(10);
c.stampa();
return 0;
}
Membri di Classe Const
I membri di classe const sono variabili che non possono essere modificate dopo l’inizializzazione.
class Esempio {
public:
const int costante;
Esempio(int val) : costante(val) {}
};
Classi e friend
di una Classe
La parola chiave friend
permette a una funzione o a un’altra classe di accedere ai membri privati e protetti di una classe.
class Amico {
private:
int segreto;
public:
Amico(int s) : segreto(s) {}
// Dichiarazione di un friend
friend void mostraSegreto(const Amico& a);
};
// Funzione friend
void mostraSegreto(const Amico& a) {
std::cout << "Il segreto è: " << a.segreto << std::endl;
}
In conclusione, la programmazione ad oggetti in C++ è una potente metodologia che offre flessibilità e organizzazione nel codice. Comprendere i concetti di base come classi, oggetti, costruttori, modificatori di accesso e friend è essenziale per scrivere codice efficiente e manutenibile.
Riferimenti
- cplusplus.com - classes I
- cplusplus.com - classes II
- cplusplus.com - special classes
- Friendship and inheritance
Quest'opera è distribuita con Licenza Creative Commons Attribuzione - Condividi allo stesso modo 4.0 Internazionale Theme Moonwalk