Aggiungiamo qualcosa di funzionante al nostro programma

Ricapitolando, un programma in C++ deve includere almeno la funzione principale main().
Il programma minimo che possiamo scrivere in C++ è rappresentato dalla funzione main() vuota.
Ricordiamoci che la funzione principale si deve chiamare main, e in nessun altro modo, e deve restituire un valore intero:
int main() //funzione principale main
{ //inizio del blocco di codice

/* qui ci va il codice */

    return 0; //restituisce l'intero 0
} //fine del blocco di codice

Quello appena riportato, come già visto, è il programma più piccolo che possiamo scrivere in C++, e contiene solo la funzione main che, ripetiamo, dev'essere presente in tutti i programmi C++, dai più semplici ai più complessi, poiché è la prima funzione ad essere chiamata (ed il codice al suo interno il primo ad essere eseguito) quando lanciamo il programma.

Tuttavia il nostro programma non fa altro che partire e terminare. Vediamo di renderlo un minimo più interessante, facendogli mostrare un output a schermo.
#include <iostream>

int main()
{
    std::cout << "Finalmente si vede qualcosa! " << std::endl;

    return 0;
}

Proviamo a compilare il programma e poi ad eseguirlo.
Come avrete notato all'esecuzione non succede niente, al massimo riuscirete a percepire la finestra che si apre per un istante, e si chiude subito dopo impedendovi di leggerne il contenuto.

Questo perché il programma stampa la stringa e subito dopo si chiude.
Per far sì che la scritta resti a schermo dobbiamo aggiungere un'altro paio di linee.
Non vi preoccupate se per ora il contenuto del programma non vi è chiaro, fra poco vedremo cosa significano le nuove righe.

#include <iostream>

int main()
{
char carattere;

std::cout << "Secondo tentativo! " << std::endl;

std::cin >> carattere;
}

Provate a ricompilare ed eseguire il programma appena modificato.
Come potete vedere ora la finestra non si chiude ma resta in attesa che voi inseriate un carattere. Inserendo un carattere e premendo invio la finestra si chiude.

Una nota per gli utilizzatori del sistema operativo Linux. Se la finestra non dovesse essere mostrata, basta aprire un terminale e spostarsi all'interno della directory che contiene il progetto con i files sorgenti e l'eseguibile.
Nel caso di Eclipse il percorso di default è "/home/tua_home/workspace/nome_progetto/Debug/".
Una volta giunti alla Directory finale, ci basterà inserire il nome dell'eseguibile preceduto da ./ e vedremo la nostra bella stringa a schermo, con tanto di prompt in attesa dell'inserimento di un carattere da tastiera.
Se ad esempio il progetto si chiama prova01 ci basterà scrivere ./prova01

Chiusa questa piccola parentesi, vediamo nel dettaglio cosa fa il programma.

La prima riga che incontriamo è #include <iostream>.
#include è una direttiva del preprocessore, il quale processa un file sorgente prima di eseguire la compilazione principale.
In altre parole #include &ld;iostream> dice al compilatore di processare il file iostream prima di procedere alla compilazione.
iostream non è altro che un file sorgente che contiene al suo interno delle funzioni che svolgono compiti specifici.
In generale, quando i nostri progetti saranno abbastanza grandi da suddivideli in più files, e avremo la necessità di richiamare in uno di questi files delle funzioni presenti in un secondo files, dovremo includere il secondo file nel primo, attraverso la direttiva #include.

In questo caso stiamo includendo un file, dal nome iostream, facente parte della libreria standard del C++, che contiene delle funzioni utili a gestire lo streaming input e output.
Dovremo includere questo file ogni volta che nel nostro programma vorremo stampare del testo a schermo, o ricevere un input da tastiera.
Un altro esempio di libreria di funzioni è fstream, che permette al nostro programma di poter lavorare con i files (ad esempio di aprire un file di testo, di modificarlo e di salvarlo).

Andando avanti nella lettura del codice incontriamo due istruzioni, std::cout ed std::cin (a dir la verità anche std::endl, che tralascio momentaneamente per comodità).
Queste funzioni sono presenti all'interno del file iostream che abbiamo incluso nel nostro programma.
Provate a commentare la riga #include facendola precedere dal simbolo che identifica un commento //, in questo modo il compilatore penserà che si tratta di un commento e la salterà.
Provate ora a ricompilare il programma e riceverete un errore, perché non avendo incluso il file iostream il compilatore non riesce a trovare le funzioni std::cin ed std::cout.

Ho saltato volutamente la riga char carattere perché le variabili sono un argomento importante e lo affronteremo più avanti in un post dedicato tutto a loro.

Non è difficile a questo punto intuire il ruolo svolto dalle due istruzioni. Ma prima di analizzarle più nel dettaglio voglio parlare brevemente dei namespace.
Come abbiamo già detto, nei progetti di una certa mole è impossibile mantenere tutto il codice in un unico file, perciò si ricorre alla suddivisione del codice in diversi files sorgenti.
Tuttavia in questi casi, soprattutto quando sono più persone a lavorare ad uno stesso progetto, o quando si utilizzano librerie scritte da altri, è possibile che due o più funzioni nei diversi files abbiano lo stesso nome.

Facciamo un esempio pratico per capire meglio.
Ipotizziamo che io ed un mio amico stiamo lavorando ad un videogioco, ad esempio un clone di Pacman.
Io mi occupo di scrivere le funzioni per gestire l'intelligenza artificiale (non molta a dir la verità) dei nemici, mentre lui si sta occupando di scrivere le funzioni per mostrare delle informazioni sullo schermo.

Ora, io sto lavorando su un file di nome nemici.cpp mentre lui sta lavorando ad un file di nome informazioni.cpp.

Mettiamo che mi trovo al punto di dover scrivere una funzione che calcola la traiettoria che devono seguire i fantasmini per catturare pacman, e decido di chiamarla calcola().

Nel frattempo il mio amico deve scrivere una funzione per calcolare il punteggio e anche lui, ignaro del fatto che io abbia già utilizzato quel nome, decide di chiamarla calcola().

Dopo un po' di giorni finiamo di scrivere ognuno il rispettivo file di funzioni e decidiamo di provarle, includendo i files nel file main.cpp del nostro progetto, che contiene l'ormai famosa funzione main():
#include "nemici.h" //includiamo i sorgenti che contengono le funzioni
#include "informazioni.h"

int main()
{
//...altro codice...

calcola(); //chiama la funzione calcola, e qui casca l'asino!

return 0;
}
Come possiamo vedere i files sorgenti inclusi hanno una forma leggermente diversa da come forse vi aspettavate, ma per ora non è il caso di preoccuparcene, arriverà il momento di parlare anche di questo.
Concentriamoci invece sulla chiamata della nostra funzione calcola().
Entrambi i files inclusi, il mio e quello del mio amico, contengono una funzione calcola(), come fa il compilatore a capire quale delle due funzioni chiamare?
La risposta è che non può e, o uno di noi due cambia il nome alla funzione, oppure si utilizzano i namespace.
Un namespace non è altro che uno spazio identificato da un nome, attraverso il quale si può accedere alle funzioni (o classi, quando inizieremo a programmare ad oggetti) racchiuse al suo interno.

Perciò mi basta includere la mia funzione calcola() all'interno di un namespace (vedremo in futuro come si fa, non è tra gli argomenti più elementari), ad esempio enemies, mentre il mio amico farà lo stesso con la sua, utilizzando come namespace il nome infos.

Ora che abbiamo inserito le nostre funzioni in due namespace differenti, abbiamo un modo per dire al compilatore quale funzione richiamare, vediamolo:
#include "nemici.h"
#include "informazioni.h"

int main()
{
//...altro codice...

enemies::calcola(); //chiama la funzione calcola all'interno del namespace enemies

infos::calcola(); //chiama la funzione calcola all'interno del namespace infos

return 0;
}
Utilizzando il namespace, seguito dalla funzione, il compilatore sa quale funzione richiamare.
Ma cosa c'entra tutto questo con il nostro primo programma? Direte voi. La mia divagazione non è stata arbitraria, c'è un motivo preciso se ho affrontato ora i namespace:

Tornando al nostro codice iniziale, ricordate le due funzioni std::cin ed std::cout? Ora quei quattro puntini :: vi dicono qualcosa?
std è il namespace utilizzato all'interno della libreria iostream.
In questo modo se abbiamo altre funzioni con il nome cin, il compilatore saprà che la riga di codice si riferisce alla funzione cin contenuta nel namespace std.

Una volta racchiuse delle funzioni in un namespace, è necessario utilizzarlo per richiamare quelle funzioni, anche se non esistono altre funzioni con lo stesso nome. Per conferma provate a cancellare std:: prima di cin e di cout, vedrete che il compilatore restituirà un errore.

Tuttavia se non si hanno altre funzioni con quei nomi è scomodo dover utilizzare std:: ogni volta che vogliamo stampare una stringa o raccogliere un input da tastiere.
A questo scopo ci corre in aiuto un'altra direttiva, dal nome using.
#include <iostream>

using namespace std; //la direttiva using

int main()
{
char carattere;

cout << "stiamo facendo progressi! " << endl;

cin >> carattere;
}

La linea di codice che abbiamo aggiunto, using namespace std, in parole povere dice al compilatore di usare il namespace std implicitamente, senza che il programmatore lo debba far precedere ogni volta ad ogni funzione di iostream.
Come avrete notato infatti, abbiamo eliminato std:: da cin e da cout, e anche da endl.
Provate a compilare il programma appena modificato e vedrete che non vi darà più l'errore di prima, nonostante l'assenza di std:: .

Prima di chiudere la parentesi di using, dobbiamo vedere un'ultima cosa a riguardo.
La libreria standard del C++ è molto grande, le tre funzioni appena viste sono solo alcune delle tante presenti, e l'utilizzo di using in questo modo può rendere il programma meno performante.
E' consigliabile invece inserire solo le funzioni che andremo effettivamente ad utilizzare:
#include <iostream>

using std::cout; //includiamo solo le funzioni che ci interessano
using std::cin;
using std::endl;

int main()
{
char carattere;

cout << "stiamo facendo progressi! " << endl;

cin >> carattere;
}
In tal modo potremo utilizzare solo cout, cin ed endl senza specificare ogni volta il namespace, e il compilatore capirà che ci riferiamo a quelle funzioni specifiche; mentre se vogliamo utilizzare altre funzioni della libreria standard (e cioé tutte quelle racchiuse nel namespace std), non incluse con la direttiva using, dobbiamo specificare esplicitamente il namespace std:: facendolo precedere a queste funzioni.

Nei piccoli esercizi che vedremo la differenza di performance è nulla, perciò useremo tranquillamente la forma di using vista all'inizio, tuttavia tenete a mente che nei progetti di una certa grandezza è buona norma includere solo le funzioni che utilizzeremo nel nostro progetto.

Chiusa la parentesi using, vediamo nel dettaglio le due funzioni.

cout << "stringa da stampare " << endl;

L'istruzione cout significa qualcosa come "invia la stringa nell'output" (nel nostro caso l'output è il comune schermo del nostro computer).

I due segni <<, "minore di", proprio come fa intuire la loro forma, servono a dirigere nell'output ciò che li segue, in questo caso "stringa da stampare ".

endl è una facility che dice a cout di andare a capo dopo aver mostrato la stringa.
Facciamo una prova per capire meglio il funzionamento di endl:
#include <iostream>

using namespace std;

int main()
{
    char carattere;

    cout << "stampiamo qualcosa CON endl! " << endl;

    cout << "stampiamo qualcosa SENZA endl! ";

    cout << "stampiamo qualcosa DI NUOVO CON endl! " << endl;

    cin >> carattere;
}
Compilate il programma modificato e vedrete che l'output sarà qualcosa di simile:

stampiamo qualcosa CON endl!
stampiamo qualcosa SENZA endl! stampiamo qualcosa DI NUOVO CON endl!

Come potete vedere dopo il secondo cout il programma non va a capo, e la terza stringa viene stampata sulla stessa linea della seconda.

Avrete notato anche che tra la stringa da stampare ed endl incontriamo di nuovo i due segni di "minore di", <<.
Questo è necessario in generale ogni volta che dobbiamo concatenare diverse stringhe, perché il compilatore capisca che si tratta di due cose separate.
Ecco un esempio (tralascio la funzione main() e tutto il resto per comodità, ma ormai sapete che il codice va inserito all'interno delle parentesi graffe):
string variabile1 = "stringa di testo";
int variabile2 = 93;

//scrivo su più righe per comodità ma volendo puoi scrivere tutto su una riga:

cout << "questa è una stringa, e anche quella che segue "
   << variabile1 << "ora segue un numero "
   << variabile2 << "per ora può bastare "
   << endl;


cin >> carattere;
Il programma stampa una prima stringa, dopodiché stampa il contenuto della variabile variabile1 (che di fatto è anch'essa una stringa), dopodiché stampa un'altra stringa, dopodiché stampa il contenuto della variabile variabile2, che è un numero (93 in questo caso), ed infine conclude la stringa andando a capo utilizzando endl.

Come vedete tra le varie stringhe è presente << che serve a concatenare nell'output delle stringhe diverse, mostrandole in un'unica stringa, che in questo caso è una successione tra stringhe dirette e variabili.
In questo modo non dobbiamo utilizzare un cout diverso per ogni stringa e variabile che vogliamo mostrare a schermo.

Una volta stampata la stringa attraverso l'istruzione cout, il programma passa all'istruzione successiva, cin. Dopo aver visto che cout gestisce l'output, è facile intuire che cin gestisce l'input.
L'altra cosa che salta subito all'occhio sono le "freccette" >>, che ora puntano verso destra.
Le freccette sono seguite da una variabile, a cui abbiamo dato il nome di "carattere".

Cosa fa cin? cin attende che l'utente prema un carattere sulla tastiera e dia invio, dopodiché cin memorizza nella variabile il carattere premuto dall'utente.
Possiamo leggerla un po' come "(cin) aspetta l'input da tastiera e quando arriva (>>) salva quest'input nella variabile seguente (carattere)".
Vedremo meglio il funzionamento di cin quando tratteremo delle variabili.

Tra le altre cose che avrete notato probabilmente c'è il fatto che ogni istruzione si conclude con un punto e virgola ;

Nel prossimo post vedremo meglio la sintassi del C++, e soprattutto vedremo nel dettaglio le variabili, che in parte avete potuto vedere all'opera anche nel presente post, anche se in modo limitato.

Nessun commento:

Posta un commento