domenica 22 marzo 2015

Modern C++ - Classi e Strutture di dati

Ben tornati al corso di Modern C++ qui sulle pagine del blog di Lubit :)

Oggi introduciamo qualcosa di piu' complesso: le classi e le strutture.
Il C++ e' stato uno dei primi linguaggi di programmazione ad oggetti, oggetti intesi come contenitori di dati ed operazioni. In particolare, un oggetto e' una istanza di una classe. Ad esempio, l'oggetto "Station Wagon" e' una istanza della classe "Auto".

Una classe viene definita con la parola chiave class ed e' definita da un costruttore e da un distruttore, oltre alle variabili e alle funzioni interne, definite membri. Le funzioni e le variabili possono esser di tre tipi:
  • pubbliche (public), ovvero visibili dall'esterno
  • protette (protected), ovvero visibili dagli oggetti di classi derivate
  • private (private), ovvero utilizzabili solo dall'oggetto stesso
Il costruttore, come dice la parola stessa, si occupa di generare un oggetto, inizializzandone le eventuali variabili interne e richiamando eventuali funzioni interne. In questo modo, si esegue l'allocazione delle risorse in modo tale poi da procedere con l'esecuzione delle sue funzioni membro (per gli amanti degli approfondimenti, consiglio di leggerne di piu' qui).

Il distruttore esegue l'operazione opposta: si occupa di liberare le risorse allocate.

La dichiarazione di una classe in C++ viene effettuata nel seguente modo:

class NomeClasse
{
  public:
    NomeClasse();
    ~NomeClasse();

    void funzioneClasse();
    bool funzioneClasse2();

  private:
    bool m_flag;
    int* m_puntatore;
};

Il costruttore ha lo stesso nome della classe, mentre il distruttore ha lo stesso nome della classe, con in piu' il prefisso "~".

Al termine delle parentesi graffe, va' inserito il punto e virgola per completare la dichiarazione. La definizione delle funzioni puo' avvenire all'interno della stessa classe oppure in separata sede, come nel seguente esempio:

NomeClasse::NomeClasse()
{
  m_flag = false;
  m_puntatore = new int();
}

NomeClasse::~NomeClasse()
{
  if(m_puntatore != nullptr)
    delete m_puntatore;
}

bool NomeClasse::funzioneClasse2(){}

int NomeClasse::funzioneClasse(){}

In C++11, e' possibile inizializzarele variabili all'interno della classe stessa:

class NomeClasse
{
  public:
    NomeClasse();
    ~NomeClasse();

    void funzioneClasse();
    bool funzioneClasse2();

  private:
    bool m_flag{false};
    int* m_puntatore{nullptr};  //lo inizializzo con nullptr
};


In questo modo, non occorre inizializzare nel costruttore queste variabili, evitando di dimenticarsi di farlo.

Come gia' detto, le parole chiavi public, private e protected regolano la visibilita' delle funzioni e delle variabili. E' buona prassi mantenere le variabili private, le quali pero' sono accessibili mediante apposite funzioni public, che permettono di leggerne il valore e di modificarlo. Ad esempio,

class Auto
{
  public:
    Auto();
    ~Auto();

    void setSpeed(int speed);
    int getSpeed();

  private:
    int m_speed{0};
};

void Auto::setSpeed(int speed)
{
  m_speed = speed;
}

int Auto::getSpeed()
{
  return m_speed;
}

In tal modo, la variabile m_speed e' modificabile mediante le due funzioni setSpeed() e getSpeed(). Nell'ottica di grandi progetti, fa comodo impostare le classi nel modo suddetto, onde evitare spiacevoli inconvenienti, specialmente con la programmazione multithread.

Per creare un oggetto di tipo Auto, basta scrivere:

Auto mia_auto;    //nello Stack

Auto *auto_puntatore = new Auto();       //nell'Heap, C-Style

std::unique_ptr auto_smart(new Auto());  //nell'Heap, C++11 Style

mia_auto.setSpeed(200);

auto_puntatore->setSpeed(200);

auto_smart->setSpeed(200);

L'accesso ai membri di una classe vengono regolati mediante l'operatore "." oppure "->", nei casi in cui si tratti di variabile semplice oppure di un puntatore.

Le strutture, definite con la parola chiave struct, sono praticamente equivalenti ad una classe, i cui membri sono tutti pubblici, ovvero le variabili e le funzioni sono visibili dall'esterno:


struct Auto
{
  int m_speed{0};
  int m_doors{5};
  bool m_run{false};
};

L'accesso alle variabili viene regolato allo stesso modo di una classe. Per questa lezione e' tutto, nella prossima puntata vedremo l'overloading delle funzioni e i costruttori di copie! Alla prossima :)

Nessun commento:

Posta un commento