Home

ITA - Appunti Cpp 013 - Programmazione ad Oggetti

linux cpp

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

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

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

class Esempio {
public:
    Esempio() {
        std::cout << "Costruttore chiamato." << std::endl;
    }

    ~Esempio() {
        std::cout << "Distruttore chiamato." << std::endl;
    }
};

Costruttore di Default e 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