You are on page 1of 97

Agostino De Marco

Maggio 2009
ver. 2009.d

DRAFT

ver.2009.d Copyright A. De Marco

Appunti di programmazione
a oggetti in C++ e Matlab

DRAFT

ver.2009.d Copyright A. De Marco

Introduzione alla programmazione a oggetti

Tecniche di programmazione supportate dal C++


1.1 Paradigmi di programmazione . . . . . . . . . . . . . . . .
1.2 Programmazione procedurale . . . . . . . . . . . . . . . . .
1.2.1 Procedure e funzioni . . . . . . . . . . . . . . . . .
1.2.2 Parametri di scambio e campi di visibilit . . . . . .
1.2.3 Hello world! . . . . . . . . . . . . . . . . . . . . .
1.2.4 Un semplice programma C++ con procedure . . . .
1.3 Programmazione modulare . . . . . . . . . . . . . . . . . .
1.3.1 Creare moduli attraverso gli spazi dei nomi . . . . .
1.3.2 Lo spazio dei nomi della libreria standard . . . . . .
1.3.3 Compilazione separata . . . . . . . . . . . . . . . .
1.3.4 Gestione delle eccezioni . . . . . . . . . . . . . . .
1.4 Astrazione dei dati . . . . . . . . . . . . . . . . . . . . . .
1.4.1 Tipi definiti dallutente . . . . . . . . . . . . . . . .
1.4.2 Tipi concreti . . . . . . . . . . . . . . . . . . . . .
1.4.3 Tipi astratti . . . . . . . . . . . . . . . . . . . . . .
1.4.4 Funzioni virtuali . . . . . . . . . . . . . . . . . . .
1.5 Programmazione orientata agli oggetti . . . . . . . . . . . .
1.5.1 Un esempio: laerodinamica di un velivolo completo
1.5.2 Dalle classi concrete alle gerarchie di classi . . . . .
1.6 Cosa non abbiamo detto? . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

Ancora sulla programmazione in C++


2.1 Elementi di base . . . . . . . . . . . . . . . . . . . . . . . . .
2.1.1 Interfaccia dei programmi con il sistema operativo . .
2.1.2 Le funzioni . . . . . . . . . . . . . . . . . . . . . . .
2.1.3 Tipi nativi . . . . . . . . . . . . . . . . . . . . . . . .
2.1.4 Il preprocessore e le unit di traduzione dei programmi
2.2 Il mondo delle classi e degli oggetti . . . . . . . . . . . . . .
2.2.1 Tipi definiti dallutente e terminologia . . . . . . . . .
2.2.2 Le strutture . . . . . . . . . . . . . . . . . . . . . . .
2.2.3 Dalle strutture alle classi . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

11
11
12
13
13
16
17
18
19
20
21
23
25
25
31
35
40
41
41
46
49

.
.
.
.
.
.
.
.
.

51
52
52
54
55
55
59
59
60
62

DRAFT

Introduzione

ver.2009.d Copyright A. De Marco

Indice

Indice

2.3
2.4
2.5
2.6
3

II
4

.
.
.
.
.

Strumenti di sviluppo
3.1 Il Compilatore gcc in ambiente GNU/Linux o Cygwin
3.1.1 Il progetto GNU . . . . . . . . . . . . . . . .
3.1.2 I passi della compilazione . . . . . . . . . . .
3.1.3 Lutilit make . . . . . . . . . . . . . . . . . .
3.2 Ambienti integrati di sviluppo (IDE) . . . . . . . . . .
3.2.1 Lambiente di sviluppo Microsoft Visual Studio
3.2.2 Lambiente di sviluppo Code::Blocks . . . . .

.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.

62
62
63
63
66

.
.
.
.
.
.
.

69
69
69
70
70
71
72
72

Programmazione a oggetti con Matlab


Una panoramica su Matlab
4.1 Elementi di base . . . . . . . . . .
4.2 Toolboxes e Simulink . . . . . . .
4.3 Sessioni di lavoro . . . . . . . . .
4.4 Usare lhelp . . . . . . . . . . . .
4.5 Nozioni e notazioni fondamentali .
4.5.1 Le variabili . . . . . . . .
4.5.2 Matrici e vettori . . . . . .
4.5.3 Altri tipi di dati . . . . . .
4.5.4 Le funzioni in Matlab . . .
4.6 Operazioni di Input / Output . . .

77
.
.
.
.
.
.
.
.
.
.

79
79
80
81
84
84
84
86
87
88
91

Programmazione a oggetti in Matlab


5.1 Strutture dati di Matlab . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2 Classi e oggetti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.3 Esempi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

93
93
94
94

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

DRAFT

ver.2009.d Copyright A. De Marco

La libreria standard . . . . . . . . . . . . . . . . . .
Le librerie Boost . . . . . . . . . . . . . . . . . . .
Esempi . . . . . . . . . . . . . . . . . . . . . . . .
2.5.1 Una classe completa per la lettura dati da file
Come imparare il C++ . . . . . . . . . . . . . . . .

A. De Marco

Introduzione
Questo non un manuale di programmazione C++ o di Matlab, ci vorrebbe troppo tempo
e impegno per scriverne uno vero. Ho solo raccolto alcune esperienze maturate nel corso
degli ultimi anni di lavoro con questi linguaggi. Alcuni esempi sono molto semplici, ma
dovrebbero servire a mettere in luce alcuni degli aspetti pi importanti che riguardano la
programmazione a oggetti.
Di sicuro questi appunti non sono una guida per il principiante, anche se ci sono tutte
le indicazioni fondamentali su come reperire informazioni sulluso corretto dei comandi;
tuttavia anche chi agli inizi potrebbe trovare qualche spunto interessante per avvicinarsi
al mondo delle classi, degli oggetti, dei puntatori e della programmazione generica.

DRAFT

pur vero, daltra parte, che per gli studenti di ingegneria dellarea industriale (meccanica, aerospaziale, navale, chimica, gestionale, eccetera) non facile trovare manuali
che trattino la programmazione in C++ in maniera adeguata al loro percorso formativo.
Spesso i corsi di informatica impartiti ai primi anni delle facolt di ingegneria finiscono
per utilizzare il linguaggio C++ come mero strumento di codifica, con il quale mostrare
gli aspetti di base del mondo della programmazione dei calcolatori elettronici. Ad esempio, quasi sempre si propone lesercizio di implementare algoritmi di ordinamento di un
vettore o di manipolazione di matrici. Per la soluzione di simili problemi nei corsi di
informatica si propone spesso il C++ perch facile trovare strumenti di lavoro interattivi
gratuitamente scaricabili da internet come, ad esempio, Dev-C++ [29], Code::Blocks
[28] o Microsoft Visual C++ (200X) Express [27] che permettono di imparare a compilare codice C++ in poco tempo e offrono la possibilit di mettere subito in pratica i
concetti generali della programmazione. Sia chiaro, ci cosa buona e giusta, anzi
assolutamente necessario per i programmatori alle prime armi, e rientra negli scopi dei
corsi sui fondamenti dellinformatica. Ci che gli studenti spesso non arrivano a comprendere, per, la straordinaria potenza di un linguaggio come il C++, che supporta la
programmazione a oggetti e permette di esplorare delle tecniche di programmazione allo stesso tempo avanzate ed eleganti. Non raro ascoltare uno studente del terzo anno

ver.2009.d Copyright A. De Marco

La trattazione di alcune tecniche di programmazione orientata agli oggetti si serve dei


linguaggi C++ e Matlab. Ovviamente, esistono gi numerosi manuali che si occupano
di C++, sia a livello introduttivo che a livello avanzato. Anche sullambiente Matlab e
sul suo linguaggio di programmazione esiste una vastissima possibilit di documentarsi,
dal potente sistema di help online ai numerosi manuali e libri di testo. Va osservato per
che i manuali di livello avanzato, in particolare per il C++, molto spesso sono destinati a
programmatori professionisti o, in generale, alla platea degli utenti informatici di media ed
elevata esperienza. Il lettore trover una lista dei manuali pi importanti nella bibliografia,
primo fra tutti il libro di Bjarne Stroustrup [1], il ricercatore di origine danese che ha
ideato il linguaggio.

Introduzione

di ingegneria aerospaziale che dichiara di aver studiato il C++ ma nega di conoscere il


concetto di classe. Per chi conosce la programmazione a oggetti ci risulta quanto meno
sorprendente. Da questo punto di vista ho ritenuto necessario, dunque, proporre unoperazione di ampliamento delle conoscenze informatiche del lettore meno esperto, ma
comunque in possesso delle nozioni di base sulla programmazione, introducendo con i
modi dellingegnere la programmazione a oggetti, prima in C++ e successivamente nel
linguaggio Matlab (che la maggioranza degli ingegneri conosce).
Come poter fare esperienza al riguardo? Ponendosi, ad esempio, un problema concreto e decidendo di risolverlo se il caso di farlo con una tecnica di programmazione
orientata agli oggetti, attraverso un programma di calcolo in C++ o in Matlab.
La prima parte di questi appunti tratta della programmazione orientata agli oggetti
(Object-Oriented Programming, OOP) e si serve del linguaggio C++ sia per spiegarne i
vari aspetti sia per fornire direttamente degli esempi pratici.
La seconda parte, dando per noti i principi di base della programmazione Matlab, ne
presenta le caratteristiche (alquanto recenti) che permettono di costruire classi e sviluppare programmi a oggetti.
Ho cercato di essere il meno formale possibile e mi sono limitato a dare esempi concreti, con qualche omissione di parti di codice troppo lunghe da riportare integralmente e
non fondamentali ai fini della comprensione del particolare concetto. Spero che ci non
porti a maggiore confusione. Non si trover qui una descrizione formale della grammatica
dei linguaggi C++ e Matlab, per le quali si rimanda ai manuali specialistici.
* Le parti di testo in carattere ridotto trattano argomenti su cui bene tornare solo dopo aver digerito il
resto, oppure contengono questioni meno importanti di quelle esposte in corpo normale.

Alcune spiegazioni potrebbero essere poco chiare: ne chiedo scusa e cercher di


rimediare in future edizioni. Commenti e richieste possono essere inviate allindirizzo
agostino dot demarco at unina dot it

Napoli
aprile 2009

DRAFT

ver.2009.d Copyright A. De Marco

Sicuramente ci saranno sviste e imprecisioni: sar lieto di correggerle, sperando di non


causare problemi a chi legger queste note.
Un commento alla veste grafica. Questo documento stato composto con PDFLATEX.
Si usata, con poche modifiche, la classe book; i caratteri impiegati sono Times, Helvetica
e Bera Mono.

A. De Marco

DRAFT

ver.2009.d Copyright A. De Marco

Appunti di programmazione
a oggetti in C++ e Matlab

DRAFT

ver.2009.d Copyright A. De Marco

Parte I

DRAFT

ver.2009.d Copyright A. De Marco

Introduzione alla
programmazione a oggetti

[: : :] non c niente di pi difficile da realizzare, n il cui successo sia meno


sicuro, n pi difficile da maneggiare, che iniziare un nuovo ordine di cose.
Perch il riformatore si crea nemici tra tutti quelli che traggono vantaggio dal
vecchio ordine, e solo tiepidi sostenitori tra tutti quelli che trarrebbero
vantaggio dal nuovo ordine [: : :]
Niccol Macchiavelli

DRAFT

ver.2009.d Copyright A. De Marco

(da Il principe, vi)

Capitolo 1
Tecniche di programmazione
supportate dal C++
Programmare capire.
Kristen Nygaard

Indice
1.1
1.2
1.3
1.4
1.5
1.6

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

11
12
18
25
41
49

In informatica, una tecnica di programmazione un paradigma per scrivere buoni


programmi per un insieme di problemi uno stile fondamentale di programmazione,
ovvero un insieme di strumenti concettuali per la stesura di programmi.
Lo stile di programmazione definisce e determina il modo in cui il programmatore concepisce e percepisce il programma. Per esempio, un programma scritto secondo
il paradigma della programmazione a oggetti (Object-Oriented Programming, OOP)
costituito da oggetti che interagiscono fra loro. Diversamente, un programma sviluppato secondo il paradigma della programmazione funzionale una composizione di
funzioni.
Ogni linguaggio di programmazione generalmente ispirato (e riconducibile) a un
particolare paradigma di programmazione. Per esempio, il Pascal e il C sono basati sul
paradigma della programmazione strutturata, il linguaggio Lisp su quello della programmazione funzionale, il Prolog su quello della programmazione logica e cos via. Alcuni linguaggi di programmazione sono influenzati da molteplici paradigmi; un esempio
macroscopico dato dal linguaggio Ada, che fu esplicitamente progettato per incorporare diversi concetti provenienti dalla programmazione strutturata, dalla programmazione

ver.2009.d Copyright A. De Marco

Paradigmi di programmazione

DRAFT

1.1

Paradigmi di programmazione . . . . .
Programmazione procedurale . . . . .
Programmazione modulare . . . . . . .
Astrazione dei dati . . . . . . . . . . . .
Programmazione orientata agli oggetti
Cosa non abbiamo detto? . . . . . . . .

12

Capitolo 1 Tecniche di programmazione supportate dal C++

modulare, dalla programmazione generica, dalla programmazione concorrente, e dalla


programmazione per tipi di dati astratti.
A questo punto occorre fare unimportante distinzione. Si dice che un linguaggio
supporta un dato stile di programmazione se fornisce funzionalit che rendono conveniente (ragionevolmente facile, sicuro ed efficiente) lutilizzo di tale stile. Il linguaggio
C++ stato progettato sia per consentire una efficiente programmazione di sistema (inglobando praticamente le caratteristiche del linguaggio C) sia per fornire meccanismi che
ben supportassero la programmazione a oggetti (si dice che il C++ un linguaggio di
programmazione orientato agli oggetti).
Al contrario, un linguaggio non supporta una data tecnica se per applicarla vengono richiesti uno sforzo o unabilit straordinari; un simile linguaggio non supporta, ma
semplicemente permette di utilizzare tale tecnica. Per esempio si possono scrivere programmi orientati agli oggetti in C, ma questo richiede uno sforzo improbo, in quanto tale
linguaggio non supporta direttamente questa tecnica. Supportare un paradigma non ha
solo il significato ovvio di mettere a disposizione funzionalit che permettano di usarlo
direttamente, ma anche quello, pi sottile, di fornire meccanismi di controllo statico e/o
dinamico su eventuali violazioni del paradigma.

DRAFT

ver.2009.d Copyright A. De Marco

* Il controllo dei tipi rappresenta lesempio pi ovvio di quanto detto. Lindividuazione di ambiguit e i
controlli dinamici sono anche utilizzati per estendere il supporto linguistico ai paradigmi di programmazione. Funzionalit extra-linguistiche, quali librerie e ambienti integrati di sviluppo, possono fornire ulteriore
supporto ai paradigmi.

Va ribadito che il C++ stato progettato per supportare lastrazione dei dati, la programmazione orientata agli oggetti e la programmazione generica, oltre alle tecniche di
programmazione C tradizionali. Non stato pensato invece con lintento di forzare tutti i
programmatori a seguire un particolare stile di programmazione.
I paragrafi seguenti prendono in considerazione alcuni stili di programmazione, dal
paradigma della programmazione procedurale al paradigma della programmazione a oggetti. Un aspetto importante sar quello di arrivare a distinguere due tra le tecniche pi
usate per il design dei programmi: il cosiddetto approccio Top-Down, tipico della programmazione strutturata e procedurale, e lapproccio Bottom-Up, tipico della programmazione a oggetti.
Allo stesso tempo lattenzione sar rivolta, inizialmente, al C++ data limportanza che
questo linguaggio riveste oggi nel campo della programmazione. Pi avanti, nella seconda
parte verranno presentate le nuove caratteristiche del linguaggio Matlab che supportano
la programmazione a oggetti.

1.2

Programmazione procedurale

Il paradigma della programmazione procedurale il seguente:


Decidi quali procedure ti occorrano;
individua e utilizza i migliori algoritmi possibili.
Il punto focale di questa tecnica lelaborazione, cio lalgoritmo necessario a svolgere
la computazione desiderata.
A. De Marco

1.2 Programmazione procedurale

1.2.1

Procedure e funzioni

Nella programmazione procedurale si organizzano i programmi come sequenze o combinazioni di sottoprogrammi in generale funzioni creando dei blocchi di codice
che interagiscono fra di loro opportunamente. Questi blocchi sono identificati da un nome e sono racchiusi da dei delimitatori. Di fatto, i delimitatori definiscono un campo di
visibilit (detto anche scope) delle variabili definite nel blocco di codice.
I vari linguaggi supportano il paradigma di programmazione procedurale fornendo gli
strumenti per passare argomenti alle funzioni e ottenere dei valori di ritorno. A seconda
del linguaggio e dei ruoli che le funzioni e le procedure hanno allinterno del linguaggio
stesso, il nome del generico sottoprogramma sar proprio il nome di una funzione o di
una procedura. Ad esempio nel linguaggio Fortran un sottoprogramma, denominato ad
esempio Task1, delimitato dalle istruzioni SUBROUTINE Task1 ed END SUBROUTINE. In
Fortran esistono anche le funzioni, in altre parole dei sottoprogrammi che ritornano un
valore, definiti dalla parola chiave FUNCTION.
Il nome programmazione procedurale deriva dal linguaggio COBOL, che stato
il primo ad utilizzare questo concetto. Le procedure possono essere definite in modo
da accettare argomenti, detti anche parametri di scambio. In fase di esecuzione di un
programma i parametri di scambio sono dati da una lista di variabili fornite alle procedure
attraverso le istruzioni di chiamata. Dal punto di vista dei sottoprogrammi, i parametri
di scambio sono delle variabili locali, i cui valori, oltre che ad essere forniti dallesterno
del blocco di codice, possono essere eventualmente esportati. Ad esempio, in Fortran le
istruzioni

Valori esportati
attraverso gli argomenti
di una procedura

ver.2009.d Copyright A. De Marco

Parametri di scambio

x1, x2, x3, out


1d-1; x2 = -0.5
Task1(x1,x2,out)
out/2.

potrebbero voler dire che la chiamata alla procedura Task1 attraverso listruzione CALL
serve ad eseguire un insieme di calcoli che porteranno ad assegnare un certo valore alla
variabile out. Essa dichiarata ed visibile nella parte di codice su riportata ed ha senso
utilizzarla dal punto immediatamente successivo alla chiamata a Task1. In questo caso
out, che compare come terzo parametro di scabio di Task1, stata utilizzata per contenere
un valore esportato dalla procedura.
Una procedura nel linguaggio C++ semplicemente una funzione che non ritorna
valori. Essa assume la forma:
void
{

Sottoprogrammi e
funzioni

hNomeProcedurai(hlista di parametrii)

hdichiarazioni e istruzionii

in cui si esplicita che il tipo di ritorno void. In altri termini, questa funzione non ritorna
alcun valore.

1.2.2

Parametri di scambio e campi di visibilit

pensabile concepire due tipi di parametri di scambio per le funzioni e le procedure:


quelli scambiati per valore e quelli scambiati per riferimento. Nei primi vengono passate delle copie di valori che, se modificati, non vengono comunque salvati al termine
Appunti di programmazione a oggetti in C++ e Matlab

Passaggio dei parametri


per valore e
per riferimento

DRAFT

REAL
x1 =
CALL
x3 =

13

14

Regole di passaggio dei


parametri in C e C++

In C++ gli argomenti di


tipo puntatore realizzano
lo scambio per
riferimanto

Capitolo 1 Tecniche di programmazione supportate dal C++

del sottoprogramma. Nellesempio precedente in linguaggio Fortran, se le variabili x1 ed


x2 fossero passate a Task1 per valore significherebbe che allinterno di questa sarebbero
visibili due copie dei loro rispettivi valori, 0.1 e -0.5. Una eventuale modifica di questi
ultimi allinterno della definizione della procedura verrebbe persa una volta che il controllo del flusso del programma passa dallistruzione di terminazione di Task1 nuovamente al
codice chiamante (istruzione successiva alla istruzione CALL).
Con i parametri scambiati per riferimento, la procedura riceve lindirizzo di una data
variabile in memoria, che pu quindi essere modificata effettivamente e permanentemente. In Fortran, ad esempio, il passaggio dei parametri di scambio per riferimento avviene
di default, salvo i casi in cui venga esplicitamente richiesto altrimenti nella definizione
delle SUBROUTINE e delle FUNCTION. In C ed in C++ lo scambio dei parametri avviene
per valore. Quando necessario scambiare parametri per riferimento si ricorre alluso dei
puntatori. Nel frammento di codice che segue lesempio precedente fornito in Fortran
viene replicato in linguaggio C++:
float x1, x2, x3;
float *out;
x1 = 0.1; x2 = -0.5;
Task1(x1,x2,out);
x3 = (*out)/2.0;

La variabile out di questultimo esempio di tipo puntatore a float perch dichiarata


attraverso loperatore * (asterisco) che segue la parola chiave float e precede lidentificatore out. In questo contesto si dice che * un operatore unario di dichiarazione (da
ritenersi applicato al nome della variabile). Di fatto, la variabile puntatore out contiene
un indirizzo di memoria, in particolare punta ad una porzione di memoria adatta a contenere un valore di tipo float. Perch la funzione Task1 possa esportare, attraverso out, un
valore verso il codice chiamante essa va definita nel modo seguente:

DRAFT

ver.2009.d Copyright A. De Marco

void Task1(float v1, float v2, float *output)


{
int j = 7;
v1 = -0.02; // modificano variabili scambiate
v2 = -j*v1; // per valore (x1 ed x2 non vengono modificate)
// ...
*output = huna qualsiasi espressione che d un floati;
}

Dereferenziazione delle
variabili puntatore

Le parentesi graffe, { }, esprimono il raggruppamento in C++. In questo caso tali parentesi indicano linizio e la fine del corpo della funzione. Si noti come nella definizione delle
funzioni i parametri di scambio possano essere nominati diversamente dalle corrispondenti variabili passate dal codice chiamante. In questo esempio listruzione di chiamata
Task1(x1,x2,out) mentre nella definizione di Task1 si usano i nomi v1, v2 ed output.
Nellultima istruzione della definizione di Task1 loperatore * che precede output
usato come operatore unario di dereferenziazione (de-referencing): in pratica esso permette di eseguire unoperazione di inserimento di un valore di tipo float in unarea di
memoria puntata da output. Al termine della funzione Task1, quando il controllo passa
al codice chiamante, questarea di memoria conterr il valore appena calcolato allinterno
di Task1 e ad esso punter la variabile out. Ecco il senso di aver dichiarato out come
puntatore a float. Nellistruzione immediatamente successiva del codice chiamante si
ha x3 = (*out)/2.0 in cui loperatore * dereferenzia out. In questo caso si dice che con
A. De Marco

1.2 Programmazione procedurale

si esegue unoperazione di estrazione del valore float puntato dalla variabile out.
Tale valore viene convenientemente usato nellistruzione di assegnamento della variabile
x3.
In C++ loperazione opposta di quella di de-referencing quella cosiddetta di estrazione dellindirizzo di una variabile. Ci realizzato dalloperatore & applicato al nome
di una variabile. Il risultato un valore che lecito assegnare ad una variabile di tipo
puntatore. Il seguente frammento di codice perfettamente lecito:

15

*out

Operatore & di
estrazione dellindirizzo

double x = 3.5;
double *p = &x;

In linguaggi come il Pascal o il Fortran, ad esempio, le procedure sono distinte dalle


funzioni perch non restituiscono un valore associato al nome della procedura stessa, e
non possono quindi apparire a destra di una istruzione di assegnazione. Nel linguaggio
C, cos come nel C++, esiste solo la funzione, che pu o meno restituire valori; in pratica
una funzione che non restituisce valore una procedura.
Allinterno di un sottoprogramma possiamo generalmente definire delle variabili locali, che vengono deallocate al termine del sottoprogramma stesso (variabili automatiche).
Il loro contenuto viene quindi perso se non salvato o trasmesso altrimenti. La variabile
j, dichiarata, definita ed usata nella definizione precedente di Task1, una variabile automatica. Essa ha campo di visibilit che si estende alla sola definizione della funzione in
cui dichiarata.
Si osservi che secondo lo standard del linguaggio C++ si pu limitare il ciclo di vita
di una qualsiasi variabile dichiarandola allinterno di una porzione di codice delimitata da
due parentesi graffe. Il seguente frammento di programma perfettamente lecito:

Variabili locali alle


funzioni ed alle
procedure

* interessante esaminare una situazione che si sarebbe avuta se nellesempio precedente avessimo
dichiarato allesterno del blocco, prima delle parentesi graffe, il puntatore p e se, viceversa, avessimo dichiarato e assegnato la variabile a allinterno del blocco. In quel caso, lassegnazione p = &a allinterno
del blocco sarebbe stata ancora lecita ma stavolta a sarebbe risultata out-of-scope dopo la chiusura della
seconda parentesi graffa. Al contrario, p sarebbe rimasto ancora in vita. In una situazione del genere si
parla di dangling reference (riferimento penzolante), cio il valore del puntatore quello di un indirizzo
non pi corrispondente ad a ma ad unarea di memoria probabilmente destinata ad un uso diverso.
Appunti di programmazione a oggetti in C++ e Matlab

Definire limiti di visibilit


in C++ attraverso le
parentesi graffe

Dangling reference

DRAFT

In C++ possibile dichiarare variabili in qualsiasi punto del codice sorgente. Nel punto
in cui la variabile d dichiarata ed assegnata il puntatore p non pi visibile. Le parentesi
graffe che fissano il ciclo di vita della variabile p definiscono a tutti gli effetti un blocco
di codice. Questo blocco non ha il ruolo di una procedura ma soltanto quello di definire
linizio e la fine della visibilit delle variabili in esso definite. Va osservato che nellinterno del blocco risultano visibili le variabili dichiarate esternamente (nello stesso file
sorgente). Alla variabile a viene applicato loperatore unario & di estrazione di indirizzo
per poter assegnare un valore lecito alla variabile locale p, di tipo puntatore a double.

ver.2009.d Copyright A. De Marco

double a, b, c;
a = 3.5;
{
double *p;
b = -1.2;
p = &a; // & estrae lindirizzo di a (un possibile valore di p)
c = (*p)+b;
}
double d = c; // qui p out-of-scope

16

Capitolo 1 Tecniche di programmazione supportate dal C++

Limiti di scope per


variabili definite
allinterno dei cicli

Il concetto di ambito visibilit limitato ad un blocco trova riscontro, ad esempio, nei


costrutti ciclici. Si esamini il seguente frammento di programma:
int k = 1;
float v = 1.5;
float *pf;
for(unsigned int i=0; i<10; i++)
{
float w;
w = (k++)*v - 1.0;
Task1(v, w, pf);
// stampa sul canale di output standard
cout << "Task1 restituisce: " << *pf << endl;
}
// qui i e w sono out-of-scope

Design dei programmi


secondo lapproccio
Top-Down

Sia la variabile intera i, che serve da contatore delle iterazioni, che la variabile w che
viene passata a Task1 sono inaccessibili allesterno delle parentesi che delimitano il ciclo
di for. Si dice che esse sono out-of-scope. Si noti come la variabile v, che prima viene
usata per assegnare w e poi viene passata a Task1, sia visibile allinterno del ciclo. Lo
stesso dicasi per la variabile pf.
Dal punto di vista dellorganizzazione del programma le funzioni vengono utilizzate
per creare ordine in una selva di algoritmi. Gli algoritmi stessi sono scritti utilizzando chiamate di funzioni e altri strumenti del linguaggio. Il progetto di un programma
secondo il paradigma della programmazione procedurale segue un approccio di tipo TopDown: si individuano i passi fondamentali dellalgoritmo che il programma deve eseguire
e per ognuno si sviluppa unapposita procedura; ciascuna procedura, a seconda della sua
complessit, viene a sua volta sviluppata secondo lo stesso criterio.

1.2.3
Funzione main

Hello world!

Il programma pi piccolo che si possa scrivere in C++ :

DRAFT

ver.2009.d Copyright A. De Marco

int main() { }

I/O standard con

Definisce una funzione che si chiama main, la quale non prende argomenti e non fa nulla.
Ogni programma C++ deve avere una funzione che si chiama main. Il programma inizia
eseguendo quella funzione. Il valore di tipo int restituito da main, se esiste, il valore che
il programma restituisce al sistema. Se non viene restituito alcun valore, il sistema ne
ricever (secondo lo standard ISO) uno che indica una terminazione corretta. Un valore
diverso da zero restituito da main indica una terminazione non corretta. La funzione main
una funzione speciale che, in C come in C++, si interfaccia con il sistema operativo ed
detta, tecnicamente, entry point del programma eseguibile.
Tipicamente un programma produce qualche messaggio. Ecco un programma che
scrive il famoso tormentone Hello world! sul canale di output standard:

iostream

#include <iostream>
int main()
{
std::cout << "Hello world!\n" ;
}

A. De Marco

1.2 Programmazione procedurale

17

La riga #include <iostream> dice al compilatore di includere le dichiarazioni delle


funzioni standard per lingresso/uscita (I/O) su canali di caratteri (stream) che si trovano
nel file iostream. Senza queste dichiarazioni lespressione std::cout << "Hello world
!\n" non avrebbe senso. Loperatore << (metti su) scrive il suo secondo argomento
(quello alla sua destra) sul primo. In questo caso si ha che la stringa letterale "Hello
world!\n" viene scritta sul canale di uscita predefinito std::cout, tipicamente, una finestra di console. Una stringa letterale del C++ una sequenza di caratteri delimitata da
doppie virgolette. In una stringa letterale il carattere \ (barra inversa) seguito da un altro
carattere denota un singolo carattere speciale. In questo caso \n il carattere di fine riga
(newline) cosicch i caratteri stampati sono "Hello world!" seguiti da un fine riga.

1.2.4

Un semplice programma C++ con procedure

Rispetto al semplicissimo esempio precedente si pu fare molto di pi. Ecco un esempio


di semplice programma in C++ decomposto in procedure e funzioni.
// Forward declarations
bool options(const int, const char**);
void PrintHelp(void);
void Task1(void);
void Task2(void);
// Global data
const char *hostname = "localhost";
int port = 5501;
realtime = false;
int main(const int argc, const char *argv[])
{
bool success = false;

return 0;
}
// Define here functions: options, PrintHelp, Task1, Task2
// ...

Qui la main, oltre che a ritornare un valore al sistema, ne riceve due: argc e argv (i
nomi di questi due argomenti sono quelli usati dalla maggior parte dei programmi). Pi
avanti si approfondir questaspetto con un esempio pi dettagliato. Qui basta sapere i due
argomenti di main, passati alla funzione dal sistema operativo, permettono al programma
di risalire alla riga di comando completa utilizzata dallutente per lanciarne lesecuzione.
Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

// *** EXECUTE TASKS *** //


Task1();
Task2();

ver.2009.d Copyright A. De Marco

// *** PARSE COMMAND LINE OPTIONS *** //


success = options(argc, argv);
if (!success) {
PrintHelp();
exit(-1);
}
realtime = true;

18

Capitolo 1 Tecniche di programmazione supportate dal C++

Il programmatore ha predisposto in questo caso la funzione options che prende direttamente in consegna gli argomenti di main e li elabora restituendo un valore booleano.
Un valore vero ritornato da options dovrebbe indicare una riga di comando ben scritta
(lecita). In caso contrario viene invocata la procedura PrintHelp, che non ha bisogno di
ritornare alcun valore perch probabilmente scriver semplicemente una serie di messaggi sul canale di output standard. Per come fatta qui la funzione main, ad una riga di
comando non lecita corrisponde la stampa di un testo di aiuto e linterruzione immediata
dellesecuzione tramite la funzione exit (il cui argomento -1 viene ritornato al sistema).
Se il controllo del flusso di operazioni del programma supera la fase di lettura della riga di
comando, allora verranno eseguite le procedure Task1 e Task2, e verr restituito al sistema
un valore di ritorno nullo con listruzione return 0.
Il programmatore evidentemente abbastanza sicuro che queste ultime non diano luogo a condizioni eccezionali, non preoccupandosi (apparentemente) di controllarne lesito. Le due funzioni probabilmente assolvono a compiti semplici e non c bisogno che
ritornino valori.
Questo programma, che pu essere memorizzato, ad esempio, in un file main.cpp,
ovviamente incompleto. Andrebbero definite nello stesso file le funzioni options,
PrintHelp, Task1, Task2, soltanto dichiarate prima della definizione della funzione main
(le cosiddette forward declarations). Tra queste solo la funzione options ritorna un valore, che di tipo bool; le altre sono a tutti gli effetti delle procedure: esse definiscono
delle variabili interne ed, al pi, manipolano delle variabili globali come hostname, port
e realtime. In questo caso, le variabili globali sono quelle definite allesterno di tutte
funzioni definite nel file main.cpp. Esse vengono assegnate tipicamente in sede di dichiarazione (quasi sempre allinizio del file, prima della definizione di main) e risultano
visibili ovunque nel file sorgente (hanno global scope). In particolare le variabili globali
verranno usate nelle definizioni delle singole funzioni utilizzate da main.

DRAFT

ver.2009.d Copyright A. De Marco

1.3

Programmazione modulare

Nel corso degli anni, lenfasi nella progettazione dei programmi si spostata dal progetto
delle procedure allorganizzazione dei dati. Tra le altre cose, questo riflette un un aumento
della dimensione dei programmi. Un insieme di procedure correlate e dei dati da esse
manipolati spesso chiamato un modulo. Il paradigma della programmazione modulare
dunque:
Decidi quali moduli ti occorrano;
suddividi il programma in modo che i dati
siano nascosti allinterno dei moduli.
Il principio di
occultamento dei dati

Questo paradigma anche noto come il principio di occultamento dei dati (data hiding). Nei problemi la cui soluzione pu essere modellata senza raggruppare necessariamente le procedure ed i dati che esse manipolano lo stile di programmazione procedurale risulta sufficiente. Quando viene applicato il paradigma modulare le tecniche per il
progetto di buone procedure sono da applicarsi per ogni procedura in un modulo.
Se i dati sono occultati nei moduli ma, allo stesso tempo, per la costruzione degli
algoritmi vi necessit di far interagire i moduli di programma gli uni con gli altri
A. De Marco

1.3 Programmazione modulare

19

scambiando dati fra di loro , nasce lesigenza di concepire le procedure e le funzioni di


ciascun modulo come delle interfacce con lesterno (interfacce utente). Inoltre, necessario permettere di accedere alla rappresentazione dei dati di un modulo solo attraverso
linterfaccia stabilita dal programmatore ed assicurare che i moduli inizializzino i loro
dati prima che questi vengano usati.

1.3.1

Creare moduli attraverso gli spazi dei nomi

Il C++ fornisce un meccanismo per raggruppare dati, funzioni ed altre entit che siano ad
essi correlate. Questo tipo di raggruppamento viene effettuato creando i cosiddetti spazi
dei nomi (o namespace) attraverso la parola chiave namespace.
Per comprendere il ruolo di un namespace si pensi ad un modulo con il quale il programmatore si prefigge di gestire e calcolare le caratteristiche aerodinamiche di unala.
In C++ si potr scrivere:

La parola chiave
namespace

namespace Wing // interfaccia


{
bool defGeometry(string);
void setAlpha(double);
void setMach(double);
void calculate(void);
double getCL(void);
double getCD();
}
Interfaccia di un modulo

Codice utente di un
modulo

La dicitura Wing:: preposta ai nomi delle funzioni indica che queste appartengono
allo spazio dei nomi Wing. Altri usi di questi stessi nomi non interferiscono e non causano
confusione. Ad esempio luso del solo nome calculate potrebbe riferirsi ad unaltra
funzione, definita per uno scopo diverso da quello previsto dal programmatore per Wing
::calculate. Nel gergo del C++ si dice che qui i :: (doppio due punti) sono usati come
operatore di risoluzione di visibilit; la funzione calculate del namespace Wing risulta
visibile allesterno del modulo come Wing::calculate.
La definizione del modulo Wing cio la sua implementazione potrebbe essere
fornita in una parte del programma compilata separatamente:
Appunti di programmazione a oggetti in C++ e Matlab

ver.2009.d Copyright A. De Marco

if ( Wing::defGeometry("wing_geometry_1.xml") )
printf("Errore nel file di configurazione della geometria!\n");
else {
Wing::setAlphaDeg(3.0);
Wing::setMach(0.5);
Wing::calculate();
printf(" CL = %f \n CD = %f \n", Wing::getCL(), Wing::getCD());
}

Implementazione di un
modulo

DRAFT

Questa lintefaccia del modulo Wing. Si intuisce che la funzione defGeometry serve a
configurare i dati geometrici dellala e che le funzioni setAlpha e setMach servono ad
assegnare delle grandezze che definiscono la corrente che investe il corpo aerodinamico
( e M1 ). La funzione calculate probabilmente destinata ad effettuare i calcoli pi
importanti e ad assegnare valore ad alcune variabili interne al modulo Wing. Tra queste vi
compariranno, ad esempio quelle che contengono i valori dei coefficienti di portanza e di
resistenza (CL e CD ). Un frammento di codice che usa questo modulo potrebbe essere il
seguente:

20

Capitolo 1 Tecniche di programmazione supportate dal C++

namespace Wing // implementazione


{
double referenceSurface, span, aspectRatio, taperRatio, leSweep,
dihedral, geomTwist, aeroTwist;
double alpha, Mach;
double CL, CD;
const int max_size = 200;
double y[max_size];
bool defGeometry(string filename)
{

hlegge il file di configurazione in formato XMLi


return hesito della lettura filei;

}
void setAlphaDeg(double val){ alpha = val/57.3; }
void setMach(double val){ mach = val; }
void calculate(void)
{

hesegue i calcoli aerodinamicii

// verranno assegnate e manipolate le y[i]


// verranno assegnati CL e CD
}
double getCL(void){ return CL; }
double getCD(void){ return CD; }
}

DRAFT

ver.2009.d Copyright A. De Marco

Il punto chiave riguardo al namespace Wing che linterfaccia esterna risulta isolata
dalla struttura dati sottostante. In questo caso i dati occultati allinterno del modulo sono le variabili double dichiarate allinizio dellimplementazione ed assegnate nelle varie
funzioni appartenenti al namespace. I dati ed il codice delle funzioni del modulo rappresentano Wing. Lutente non ha bisogno di conoscere i dettagli di come sono gestiti i dati
allinterno di Wing. Limplementazione del modulo pu essere modificata ad esempio,
potrebbe essere migliorata la funzione calculate senza che il codice utente ne sia
influenzato.
Poich i dati sono solo una delle cose che si pu volere nascondere, la nozione
di occultamento dei dati viene estesa facilmente a quella, pi generale, di occultamento
dellinformazione; ovvero, cos come i nomi delle funzioni, anche i nomi dei tipi e di altri
elementi del linguaggio possono essere resi locali a un modulo. Di conseguenza, il C++
permette di collocare qualsiasi dichiarazione in un namespace.

1.3.2

Lo spazio dei nomi della libreria standard

Il C++ dotato di una libreria standard che mette a disposizione dei programmatori un
gran numero di funzionalit. La libreria standard usabile in ogni implementazione
completa del C++, cio con ogni sistema di compilazione che sia standard compliant,
e permette di scrivere codice portabile.
La libreria standard definita in uno spazio dei nomi chiamato std. Per questo motivo
si scrive std::cout invece che cout, cio per essere espliciti nellusare il cout standard
invece che qualche altro cout. Anche il nome std::endl definito nella libreria standard
e coincide con il carattere di newline \n.
Ogni funzionalit della libreria fornita tramite qualche file header simile al file
iostream. Per esempio:
A. De Marco

1.3 Programmazione modulare

21

#include <string>
#include <list>

rendono disponibili le classi standard string e list. Per usare queste classi al di fuori
del namespace std si dovrebbe usare il prefisso std::
std::string s = "A che ora si cena?";
std::list<std::string> phrases;

Per semplicit il prefisso std:: pu essere omesso a patto di dichiarare luso globale
dello spazio dei nomi std cos:
using namespace std ;

Questa dichiarazione pu essere fatta, ad esempio, ad un livello globale subito dopo le


direttive #include. Si dice allora che il nome di namespace std trasferito nel namespace
globale. Ad esempio:
#include <string>
using namespace std;
string s = "Perche sprecare tempo a imparare? Lignoranza e immediata!";

generalmente uno stile sconsigliato quello di trasferire nel namespace globale tutti i
nomi di un namespace. Spesso in questo documento, per abbreviare i pezzi di programmi
usati per illustrare le caratteristiche del linguaggio e della libreria, sono omesse le ripetizioni delle direttive #include e delle specifiche di std::. Si capisce dal contesto che
alcune funzionalit hanno senso solo se vengono lette le opportune dichiarazioni da file
header di libreria e se vengono resi globali alcuni nomi del namespace std.

1.3.3

Compilazione separata

e gli utenti dovrebbero includere (direttiva #include) tale file cos:


//---------------------------------------// File: user.cpp
//---------------------------------------Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

//---------------------------------------// File: Wing.h


//---------------------------------------namespace Wing // interfaccia
{
bool defGeometry(string);
void setAlpha(double);
void setMach(double);
void calculate(void);
double getCL(void);
double getCD();
}

ver.2009.d Copyright A. De Marco

Il C++ supporta la nozione del C di compilazione separata. Questa pu essere usata per
organizzare il programma in un insieme di frammenti semi-indipendenti.
Tipicamente, nella programmazione modulare si collocano le dichiarazioni che specificano linterfaccia di un modulo in un file che un nome che indica luso a cui esso
preposto. Ad esempio, le dichiarazioni di interfaccia di Wing sarebbero collocate nel file
Wing.h:

22

Capitolo 1 Tecniche di programmazione supportate dal C++

#include "Wing.h" // ottiene linterfaccia


void f()
{
if ( Wing::defGeometry("wing_geometry_1.xml") )
printf("Errore nel file di configurazione della geometria!\n");
else {
Wing::setAlphaDeg(3.0);
Wing::setMach(0.5);
Wing::calculate();
printf(" CL = %f \n CD = %f \n", Wing::getCL(), Wing::getCD() );
}
}

Per aiutare il compilatore ad assicurare la consistenza, il file che fornisce limplementazione del modulo Wing, Wing.cpp, includer anche linterfaccia:
//---------------------------------------// File: Wing.cpp
//---------------------------------------#include "Wing.h" // ottiene linterfaccia
namespace Wing // rappresentazione
{
double referenceSurface, span, aspectRatio, taperRatio, leSweep,
dihedral, geomTwist, aeroTwist;
double alpha, Mach;
double CL, CD;
const int max_size = 200;
double y[max_size];
}

DRAFT

ver.2009.d Copyright A. De Marco

// implementazione dellinterfaccia
bool Wing::defGeometry(string filename){ /* ... */ }
void Wing::setAlphaDeg(double val){ alpha = val/57.3; }
void Wing::setMach(double val){ mach = val; }
void Wing::calculate(void){ /* ... */ }
double Wing::getCL(void){ return CL; }
double Wing::getCD(void){ return CD; }

Il codice utente va inserito in un terzo file, per esempio user.cpp. I codici in user.cpp
e Wing.cpp condividono le informazioni relative allinterfaccia presentate in Wing.h, ma
i due file sono indipendenti sotto tutti gli altri punti di vista e possono essere compilati
separatamente.
La compilazione separata una questione che riguarda tutti i programmi reali. Non
semplicemente di pertinenza dei programmi che forniscono servizi, come Wing, sotto
forma di moduli. A rigor di termini, luso della compilazione separata non tanto una
questione di linguaggio, quanto piuttosto di avvalersi al meglio di una particolare implementazione del linguaggio (cio di un particolare sistema di compilazione, ad esempio
linfrastruttura dei compilatori di sistema di Linux oppure un particolare ambiente di sviluppo integrato). Ciononostante, la compilazione separata di grande importanza dal
punto di vista pratico. Il miglior approccio consiste nel massimizzare la modularit
cio creare per quanto possibile, specialmente in programmi di una certa dimensione,
tanti moduli gerarchicamente organizzati , rappresentare tale modularit dal punto di
A. De Marco

1.3 Programmazione modulare

23

vista logico per mezzo delle funzionalit del linguaggio e sfruttarla dal punto di vista
fisico per mezzo di file, per permettere unefficace compilazione separata.

1.3.4

Gestione delle eccezioni

class BadFormatFile { /* ... */ }; // tipo che rappresenta le eccezioni


// relative agli errori di lettura
// dati da file
}

in cui compare per la prima volta la parola chiave class che in C++ serve in generale
a definire nuovi tipi. In questo caso si definisce il tipo badConfigFile allinterno del
namespace.
Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

//---------------------------------------// File: Wing.h


//---------------------------------------namespace Wing // interfaccia
{
bool defGeometry(string);
void setAlpha(double);
void setMach(double);
void calculate(void);
double getCL(void);
double getCD();

ver.2009.d Copyright A. De Marco

Un programma, spesso, dopo il rilevamento di un errore deve essere concluso, perch


non possibile immaginare una maniera sensata di continuarlo. Ci pu essere ottenuto
chiamando exit(), che per prima cosa ripulisce le risorse in uso come gli stream di output
e poi conclude il programma restituendo il valore del suo argomento. Meccanismi di
gestione degli errori pi sofisticati possono realizzarsi con luso delle eccezioni, ma per
un programma di meno di 150 righe pi che sufficiente luso di messaggi derrore e
della funzione exit().
Quando un programma progettato come un insieme di moduli, la gestione degli errori deve essere presa in considerazione alla luce dellorganizzazione modulare. Va stabilito
quale modulo responsabile di un certo tipo di errore. Spesso il modulo che individua un
errore non sa quale azione intraprendere. Lazione di ripristino dipende dal modulo che ha
invocato loperazione piuttosto che dal modulo che ha riscontrato lerrore mentre cercava
di eseguire tale operazione. Man mano che i programmi crescono, e specialmente quando
viene fatto uso estensivo di librerie scritte da altri, diventano importanti degli standard di
gestione dellerrore e, pi in generale, delle circostanze eccezionali.
Si consideri ancora lesempio di Wing. Cosa dovrebbe essere fatto quando cerchiamo
di caricare dal file wing_geometry_1.xml i dati geometrici dellala e si verifica un errore
dovuto ad un formato inatteso? Chi ha scritto il modulo Wing non sa cosa lutente vorrebbe
fosse fatto in un caso simile. Lutente, daltro canto, non pu individuare il problema in
maniera coerente (se avesse potuto, avrebbe fatto in modo da evitare lerrore di lettura).
La soluzione che chi ha implementato Wing individui leventualit di un errore di lettura
da file e quindi lo comunichi allutente (sconosciuto). A questo punto il codice utente pu
essere modellato in modo di intraprendere lazione appropriata. Per esempio:

24

Capitolo 1 Tecniche di programmazione supportate dal C++

Quando riscontra un errore di lettura da file, Wing::defGeometry pu invocare il codice di gestione dellerrore, ovvero, si dice che essa pu sollervare uneccezione di tipo
Wing::BadConfigFile:
bool Wing::defGeometry(string filename)
{
bool goodFormat = false;
bool goodData = false;

hfunzione che apre e legge il file di input in formato XMLi;

// goodFormat e gooddata vengono opportunamente assegnate


if (!goodFormat) throw BadFormatFile();
// ...
return goodData;
}

Si noti che nellultimo frammento di codice si possono verificare due situazioni da gestire come errori: una la possibile incompletezza dei dati presenti nel file filename
(ad esempio chi ha composto il file XML ha dimenticato di inserire il dato dellapertura
alare), laltra leventuale cattivo formato del file. Nel primo caso la funzione Wing::
defGeometry ritorna un valore falso e spetta al codice utente preoccuparsi di controllarlo.
Il secondo caso quello di cui qui ci stiamo occupando come situazione eccezionale (in
realt entrambe le situazioni sono di questo tipo ma qui ci piace far vedere come le due
eventualit possono essere trattate in modo separato).
Listruzione throw trasferisce il controllo del programma da quel punto (e non alluscita della funzione) ad un gestore delle eccezioni di tipo Wing::BadFormatFile definito
in qualche funzione che ha invocato, direttamente o indirettamente, Wing::defGeometry.
Per fare ci il compilatore ha predisposto meccanismi che permettono al flusso del programma di risalire la catena di invocazioni fino a tornare al contesto del chiamante. Quindi
si dice che listruzione throw si comporta come un return multilivello. Per esempio:

DRAFT

ver.2009.d Copyright A. De Marco

//---------------------------------------// File: user.cpp


//---------------------------------------#include "Wing.h" // ottiene linterfaccia
void f() {
// ...
try { // qui le eventuali eccezioni sono gestite
// dal gestore definito sotto (catch)
if ( Wing::defGeometry("wing_geometry_1.xml") )
printf("Dati mancanti nella configurazione geometrica!\n");
else {
Wing::setAlphaDeg(3.0);
Wing::setMach(0.5);
Wing::calculate();
printf(" CL = %f \n CD = %f \n", Wing::getCL(), Wing::getCD());
}
}
catch (Wing::BadFormatFile) { // gestore delleccezione
// oops: errore di formato
// intraprendere lazione appropriata
// ...
}
}
A. De Marco

1.4 Astrazione dei dati

25

Se vi un errore di formato nel file wing_geometry_1.xml listruzione


if ( Wing::defGeometry("wing_geometry_1.xml") )

prima ancora di pervenire ad un valore di ritorno nellespressione condizionale, sollever


leccezione. Pertanto, la clausola catch, che fornisce un gestore per leccezione di tipo
Wing::BadFormatFile, viene necessariamente eseguita dopo loperazione di lettura da file
che ha causato un throw dallinterno della funzione Wing::defGeometry.
Luso di meccanismi di gestione delle eccezioni pu rendere la gestione degli errori
pi regolare e leggibile e si presta in maniera appropriata alla programmazione modulare. Quando un programma composto da moduli separati, e soprattutto se i moduli
provengono da librerie sviluppate da altri programmatori, la gestione degli errori va necessariamente suddivisa in due parti: (i) la segnalazione di condizioni di errore che non
possono essere risolte localmente e (ii) la gestione degli errori identificati altrove. Lautore di una libreria pu identificare errori in fase di esecuzione, ma generalmente non sa
quale operazione intraprendere. lutente della libreria, invece, sa come reagire allerrore,
ma non in grado di identificarlo altrimenti lerrore sarebbe stato gestito dal codice
utente e non lasciato alla libreria.
Le eccezioni sono il meccanismo che il C++ impiega per separare la gestione degli errori dalla segnalazione degli errori. Se le istruzioni inserite in un blocco try, o le funzioni
ivi chiamate, sollevano uneccezione, viene esaminata la parte di gestione delleccezione.
Se leccezione sollevata di un tipo trattato da un gestore di eccezioni, tale gestore viene
eseguito. In caso contrario i gestori di eccezione vengono ignorati e il blocco try si comporta come un blocco ordinario. se viene sollevata uneccezione e nessun blocco try la
tratta, il programma termina.
Fondamentalmente il trattamento delle eccezioni in C++ un modo di trasferire in
controllo a una determinata parte della funzione chiamante. Quando serve, possono essere
passate al chiamante alcune informazioni sullerrore.

La modularit un aspetto fondamentale di qualsiasi programma che non sia tanto semplice da esaurirsi in meno di un centinaio di righe ed in un solo file. Questaspetto diventa
via via pi importante al crescere delle dimensioni dei programmi e del numero di file che
li compongono. Nella progettazione delle applicazioni di successo la modularit diventa
un fattore chiave. Ciononostante, la sola suddivisione del software in moduli pu risultare
insufficiente ad esprimere in maniera pulita sistemi complessi.
Per fare ci si deve passare alla nozione di tipi definiti dallutente che rappresentano
la caratteristica fondamentale messa a disposizione dal C++. I tipi che un programmatore
C++ pu costruire a piacimento (seppur rispettando un insieme di regole, se non vuole
combinare pasticci) permettono di definire variabili che hanno un ciclo di vita del tutto
simile a quello delle variabili dei tipi predefiniti.

1.4.1

Tipi definiti dallutente

Il C++ consente di definire direttamente un nuovo tipo che si comporta (pressoch) allo
stesso modo di un tipo predefinito come int o double. Qualcuno chiama i tipi definiti
Appunti di programmazione a oggetti in C++ e Matlab

ver.2009.d Copyright A. De Marco

Astrazione dei dati

DRAFT

1.4

26

Capitolo 1 Tecniche di programmazione supportate dal C++

da utente tipi di dati astratti, ma c chi osserva che, probabilmente, meno fuorviante
chiamarli tipi definiti dallutente. Anzi, come vedremo poco pi avanti, un nuovo tipo di
dato pu anche essere confezionato in modo tale da essere chiamato tipo concreto.
Il paradigma di programmazione che utilizza i tipi definiti dallutente diventa:
Decidi quali tipi ti occorrano;
fornisci un insieme completo di operazioni per ogni tipo.

Parola chiave class

Nei casi in cui non c necessit di usare pi di un oggetto di un dato tipo, lo stile di
programmazione modulare basato sulloccultamento dei dati per mezzo dei moduli resta
sufficiente.
Un esempio immediati di possibile tipo definito dallutente quello che rappresenta
un numero complesso. Come noto, i numeri complessi hanno una parte reale ed una
immaginaria ed hanno una loro aritmetica; e gi questo permette di astrarre queste
caratteristiche peculiari in un nuovo tipo di dato a partire dal tipo predefinito double. In
C++ si pu definire il tipo complex per mezzo della parola chiave class scrivendo, per
esempio:
class complex {
double re, im;
public:
complex(double x, double y) { re=x; im=y; } //
//
complex(double x) { re=x; im=0; }
//
//
complex() { re=0; im=0; }
//
//

costruisce un complesso
da due scalari
costruisce un complesso
da uno scalare
costruisce il complesso
di default (0,0)

double real() const { return re; }


// estrae parte reale
double imag() const { return im; }
// estrae parte immaginaria
// ... eventuali altre operazioni possibili

DRAFT

ver.2009.d Copyright A. De Marco

};

Rappresentazione
privata dei dati

Variabili membro

Questa la dichiarazione di una classe complex, cio di un tipo di dato definito dallutente,
che specifica la rappresentazione di un numero complesso e di alcune operazioni ad esso
applicabili (si noti il ; dopo la fine del blocco di dichiarazioni). La rappresentazione
consiste semplicemente nei due dati double contenuti nelle variabili re ed im. Secondo
le regole del C++ la posizione in cui compaiono le dichiarazioni di queste due variabili
le rende di default dei dati privati, cio accessibili solo dalle funzioni specificate dalla
classe complex. I dati che sono in dotazione ad una particolare classe si dicono propriet
o variabili membro.
* In questa dichiarazione, oltre alla rappresentazione privata di complex dovrebbe colpire la specifica di
ben tre funzioni che hanno lo stesso nome della classe, senza valore di ritorno e ciascuna con una lista
differente di argomenti. Esse si dicono i costruttori della classe ed offrono allutente la possibilit di creare
variabili di tipo complex in vari modi. Se ne parler tra poco.

In C++ si pu dichiarare esplicitamente che delle variabili e delle funzioni sono private
con la clausola chiave private:. Ecco come un programmatore riscriverebbe la classe
complex:
class complex {
public:
A. De Marco

1.4 Astrazione dei dati

27

// costruttori
complex(double x, double y) { re=x; im=y; }
complex(double x) { re=x; im=0; }
complex() { re=0; im=0; }
// funzioni membro pubbliche
double real() const { return re; }
double imag() const { return im; }
// ...
private:
double re, im;
complex fmp(void);
// ...
};

In questo caso si ha anche la dichiarazione privata di una funzione, la fmp(), allinterno


della classe. le funzioni incorporate nei tipi definiti dallutente vengono chiamate metodi
o funzioni membro, possono operare sulle variabili membro ed eventualmente accettare
degli argomenti.
Essa solo dichiarata nella parte privata della classe e pu essere definta altrove (al
di fuori del blocco di dichiarazioni di complex o in un altro file). Potrebbe aversi, per
esempio:

Funzioni membro

// qui la classe complex e la funzione sqrt


// devono risultare dichiarate
// ...
complex fmp(void) {
double a = (re - im);
double b = sqrt(re*re - im*im);
return complex(a,b);
}

Come si certamente notato, le due funzioni membro real() e imag() sono pubbliche e corrispondono alle operazioni di estrazione della parte reale e della parte immaginaria del numero complesso. I metodi pubblici sono espressione dellinterfaccia del tipo
complex verso i suoi utenti. Ad esempio:
void nice_printc(complex z) {
cout << "Parte reale: " << z.real() << ", "
Appunti di programmazione a oggetti in C++ e Matlab

Overloading delle
funzioni

ver.2009.d Copyright A. De Marco

* Loverloading delle funzioni una delle caratteristiche pi potenti ed allo stesso tempo pi delicate del
C++. Questa funzionalit dovuta al controllo rigoroso dei tipi.

Il costruttore di una
classe

DRAFT

Si noti come le funzioni membro possano accedere alla rappresentazione della classe a
cui appartengono. Le variabili private re ed im sono visibili dallinterno della definizione
di fmp() (sarebbero addirittura modificabili, ma assolutamente sconsigliabile in un caso
del genere). Inoltre, nellultima istruzione di fmp() si costruisce al volo un oggetto di
tipo complex a partire da due scalari e lo si consegna allistruzione return.
Una funzione membro con lo stesso nome della classe di appartenenza viene chiamata
costruttore. Un costruttore definisce un modo per inizializzare un oggetto della sua classe.
La classe complex fornisce tre costruttori. Significa che gli utenti hanno tre possibilit
diverse per inizializzare un complex: lo si pu creare a partire da una coppia di double, a
partire da un double oppure si pu creare un complex con un valore di default. In pratica,
il costruttore una funzione definita pi volte con liste di argomenti diverse. Quando ci
avviene per le funzioni si dice che esse sono sovraccaricate (function overloading).

28

Capitolo 1 Tecniche di programmazione supportate dal C++

<< "Parte immaginaria: " << z.imag() << "\n";


}
int main(void) {
double x = 0.0, y = 0.0;
for(int k=0; k<10; k++){ // stampa 10 numeri complessi
x = x + k*0.2;
y = x*x*x;
complex w = complex(x,y);
nice_printc(w);
}
}

DRAFT

ver.2009.d Copyright A. De Marco

Il codice della funzione nice_printc accede (in sola lettura, parola chiave const nelle
definizioni di real e di imag) alla rappresentazione dellargomento z, una variabile di
tipo complex, attraverso le due funzioni membro pubbliche. Linvocazione di tali funzioni
viene fatta attraverso loperatore . (punto) applicato alla variabile z.
Fino a questo punto lutilit della classe complex sembra limitata. Lutente pu costruire complessi ed al massimo usarne la parte reale e immaginaria. Anche una funzione
membro privata come fmp(), se non usata direttamente o indirettamente da qualche metodo pubblico sembra del tutto inutile. In effetti ha senso definire un nuovo tipo se, oltre
a raggruppare convenientemente dei dati, si riescono a definire anche delle operazioni su
di essi che permettano allutente di costruire efficientemente dei sistemi pi complessi.
Nel caso di complex ci significa allargare lo spettro di funzioni, oltre a real() ed
imag(), che diano ai suoi utenti la possibilit di compiere delle operazioni complesse.
Non detto, in realt, che queste nuove funzioni debbano necessariamente far parte della
classe. Esse potranno semplicemente essere accreditate ad usare i suoi dati privati. In
C++ questa possibilit data dalla parola chiave friend. Si pu pensare, ad esempio, ad
una funzione sumc che permetta di sommare due complessi. Si dichiarer allinterno della
classe:
class complex {
public:
// costruttori ....
// funzioni membro pubbliche
// ...
friend complex sumc(complex z1, complex z2);
// ...
private:
// ...
};

ed altrove si definir:
complex sumc(complex z1, complex z2) {
return complex( z1.re + z2.re,
// sumc ha accesso alle
z1.im + z2.im ); // variabili private
}

Qui loperatore . (punto) applicato agli argomenti z1 e z2 permette di accedere direttamente ai valori delle variabili membro private. Questo meccanismo permesso dal fatto
che sumc stata dichiarata come funzione friend (amica) della classe. Loperazione di
somma di due complessi usata da questo frammento di codice:
A. De Marco

1.4 Astrazione dei dati

29

complex u(1.0), w(0,1.0);


nice_printc( sumc(u,w) );

Il valore ritornato da sumc di tipo complex e viene passato a nice_printc che stampa in
bella forma il risultato della somma.
Il C++ offre anche qualcosa di meglio rispetto a questa situazione. possibile infatti
definire delle funzioni che vengono invocate in maniera appropriata quando in unespressione aritmetica compaiono variabili di tipo complex. Questo permetterebbe al programmatore di scrivere del codice come quello seguente:
complex u(1.0), w(0,1.0);
complex z1, z2;
z1 = 3.5 * u - w; // prodotto per scalare e somma
z2 = complex(-1,-1) + z1*z1 + z1/z2; // prodotto e divisione
bool sure = ( z1 != z2 ); // confronto (!= significa: e diverso da?)

friend bool operator==(double, complex); // uguaglianza


friend bool operator!=(complex, double); // differenza
// ...
private:
Appunti di programmazione a oggetti in C++ e Matlab

ver.2009.d Copyright A. De Marco

class complex {
public:
// costruttori
complex(double x, double y) { re=x; im=y; }
complex(double x) { re=x; im=0; }
complex() { re=0; im=0; }
// funzioni membro pubbliche
double real() const { return re; }
double imag() const { return im; }
// funzioni amiche
friend complex operator+(complex, complex);
friend complex operator-(complex, complex); // binario
friend complex operator-(complex);
// unario
friend complex operator*(complex, complex);
friend complex operator/(complex, complex);
friend complex operator+(double, complex); // misto
friend complex operator+(complex, double); // misto
friend complex operator-(double, complex); // misto
friend complex operator-(complex, double); // misto
friend complex operator*(double, complex); // misto
friend complex operator*(complex, double); // misto
friend complex operator/(double, complex); // misto
friend complex operator/(complex, double); // misto

Overloading degli
operatori

DRAFT

Per permettere un uso legale di queste operazioni bisogna estendere, al livello del linguaggio, luso degli operatori aritmetici +, - (sia binario che unario), *, / e degli operatori
binari booleani == e != di uguaglianza e differenza. Tecnicamente si dice che bisogna
sovraccaricare questi operatori (operator overloading, cos come avviene per le funzioni) attraverso delle funzioni opportune. Il compilatore far in modo di invocare queste ultime se nelle espressioni aritmetiche trover dei dati di tipo complex. In C++ le
funzioni che permettono loverloading degli operatori devono avere un nome del tipo:
operatorhoperatorei.
Una pi conveniente dichiarazione della classe complex sar:

30

Capitolo 1 Tecniche di programmazione supportate dal C++

double re, im;


// ...
};

dove sar:
complex operator-(complex z1, complex z2) // es.: u-w
{
return complex( z1.re-z2.re, z1.im-z2.im );
}
complex operator-(complex z) // es.: -w
{
return complex( -z.re, -z.im );
}
complex operator-(double x, complex z2) // es.: 2.1-w
{
return complex( x-z2.re, -z2.im );
}
//... e cosi via

Siccome lappetito vien mangiando, sembra naturale realizzare anche loverloading di


un operatore come <<, per linserimento di un complex nelloggetto predefinito cout. In
questo modo il programmatore pu fare a meno della funzione nice_printc ed utilizzare
in un modo uniforme il meccanismo di output standard dei caratteri anche se c da stampare un nuovo tipo di dato. Loperatore di inserimento vuole alla sua destra lindirizzo di
memoria di un oggetto di tipo std::ostream messo a disposizione dalla libreria standard.
Si pu scrivere allora:
#include <iostream>
using namespace std;
class complex; // Class forward declaration

DRAFT

ver.2009.d Copyright A. De Marco

ostream& operator<<(ostream& s, const complex& z)


{
// inserisce tipi predefiniti e poi ritorna s
return s << ( << z.real() << , << z.imag() << );
}
int main()
{
complex x(1,2), y(-2,0.5), z;
y += x; // aggiunge x ad y, poi riassegna ad y
z = 8.5*x-y/x;
cout << "x = " << x << \n
<< "y = " << y << \n
<< "z = " << z << \n;
return 0;
// ridondante in ISO C++
}

Il nome std::cout il nome predefinito dellindirizzo di un oggetto di tipo std::ostream.


Il compilatore converte gli operatori che coinvolgono numeri complex in opportune
invocazioni di funzioni. Per esempio, x!=y cignifica operator!=(x,y) e 1/z significa
operator/(1,z).
In C++ la dichiarazione
A. De Marco

1.4 Astrazione dei dati

31

class complex; // Class forward declaration

posta, ad esempio, allinizio di un file, serve ad asserire (ad un livello di visibilit globale)
che nel resto del file, in una o pi definizioni di funzione che seguiranno, sar allocato un
oggetto di tipo complex. Qui la parola chiave class usata in una dichiarazione.

1.4.2

Dichiarazione anticipata
di una classe

Tipi concreti

I tipi definiti dallutente possono essere progettati in modo da soddisfare unampia variet
di esigenze. Il tipo complex visto in precedenza solo un esempio e rappresenta un primo
esercizio di scrittura di una classe. La libreria standard definisce una classe std::complex
che pu essere inclusa con la direttiva #include <complex>.
* La libreria standard definisce in realt una classe parametrica, cio un tipo cosiddetto generico, in
cui il tipo primitivo della parte reale ed immaginaria pu essere scelto dal programmatore. Ad esempio, ci
si potrebbe accontentare che la rappresentazione di questi due dati si basi sul tipo primitivo float anzich
sul double. Alla programmazione generica, che alla base delle Standard Template Libraries (STL) si
accenner pi avanti.

Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

//---------------------------------------// File: Wing.h


//---------------------------------------class Wing {
public:
// costruttori
Wing();
// dichiarazione
Wing(string filename);
// dichiarazione
// distruttore
~Wing();
// dichiarazione
// funzioni membro pubbliche
void SetAlpha(double);
// dichiarazione
double GetAlpha(void){ return alpha; } // implementazione
void SetMach(double);
// dichiarazione
double GetMach(void){ return mach; }
// implementazione
// ...
bool Init(string filename);
// dichiarazione
void Calculate(void);
// dichiarazione
double GetCL(void){ return cL; }
// implementazione
double GetCD(){ return cD; }
// implementazione
// ...
class BadFormatFile { /* ... */ }; // una classe definita in una classe
// definisce le eccezioni di formato
private:
double referenceSurface, span, aspectRatio, taperRatio, leSweep,
dihedral, geomTwist, aeroTwist;
double alpha, mach;
double cL, cD;
double *y; // puntatore ad un vettore in heap
// ...
bool loadGeometry(string);
// dichiarazione
// ...
};

ver.2009.d Copyright A. De Marco

Consideriamo un esempio pi realistico che trae spunto dallesempio del namespace


Wing e ne presenta una versione migliore. Definiamo qui una classe Wing:

32

Capitolo 1 Tecniche di programmazione supportate dal C++

Le stesse variabili utilizzate nellesempio precedente del namespace Wing sono state
poste nella parte privata della classe. Questo risponde ancora al principio delloccultamento dei dati. Quando si ha a che fare con le classi si usa anche il termine incapsulamento. Grazie allincapsulamento lutente della classe Wing pu accedere alla sua
rappresentazione ai suoi dati solo attraverso linterfaccia della classe linsieme
delle funzioni pubbliche predisposto dal programmatore di Wing. Le seguenti istruzioni
mostrano esempi duso corretto e scorretto di questa classe:
Wing w; // definisce una variabile di tipo Wing
cout << "Apertura alare (m): "
<< w.GetSpanMT() << "\n"; // bene, se GetSpanMT definita
w.span = 12.5 // errore: span e privata

Oggetti

Il costruttore di una
classe

Il distruttore di una
classe

Wing::Wing() // costruttore di default


{
// inizializza tutte le variabili private
// ...
y = new double[100] // alloca gli elementi nella
// memoria dinamica (heap)
}

ver.2009.d Copyright A. De Marco

DRAFT

In pratica, i programmatori che creano dei nuovi tipi di variabili diversi da quelli
primitivi, cio predefiniti dal linguaggio , istruiscono il compilatore C++ ad organizzare
meccanismi di gestione della memoria e delle funzioni di manipolazione dei dati. Questi meccanismi sono associati a delle singole entit, rappresentate appunto dalle variabili
stesse. Le variabili di un qualsiasi tipo sono anche dette oggetti, anche le variabili di
tipi primitivi. Ma questa terminologia esprime bene le peculiarit delle variabili appartenenti a tipi definiti dallutente. Gli oggetti sono tipicamente delle variabili appartenenti a
determinate classi, le quali possono essere definite in modo tale da permettere agli oggetti
di interagire fra di loro attraverso le funzioni membro.
Nel gergo tecnico ogni oggetto unistanza di una data classe, cio una variabile che
occupa la memoria necessaria a contenere le strutture dati e le funzioni previste dalla classe. Il costruttore la funzione invocata ogni volta che viene creato un oggetto della classe
e si occupa dellinizializzazione. Un costruttore come Wing(), che non ha argomenti,
detto costruttore di default, invocato da unistruzione come Wing w; ed effettua uninizializzazione che il programmatore ritiene di default per oggetti di questa classe. Nel caso
sia necessario fare pulizia quando un oggetto di una classe non pi visibile, possibile
dichiarare il complementare di un costruttore, detto distruttore:

Wing::~Wing() // definisce il distruttore


{
// le variabili private sono automatcamente distrutte
// tranne quelle indirizzate sullo heap
delete[] y

// libera lo spazio occupato dagli elementi


// di y per permettere un futuro riuso

Memoria dinamica
(heap) e operatore new

Il costruttore di default inizializza una nuova variabile di tipo Wing. Per fare ci alloca
nella memoria dinamica (detta anche heap) utilizzando loperatore new. La parola chiave
new del C++ permette di allocare memoria dinamica utile a contenere una variabile o una
sequenza di variabili di un determinato tipo (anche di tipi definiti dllutente). Loperazione
A. De Marco

1.4 Astrazione dei dati

33

di allocazione di memoria dinamica restituisce (alla sinistra di new) un puntatore ad essa.


Nel nostro esempio y un array di 100 elementi di tipo double. Come avverrebbe per il C,
anche nel linguaggio C++ la y non altro che un puntatore al primo elemento dellarray,
ovvero y[0]. Conservare in una variabile di tipo puntatore il risultato di new anche detto
indirizzare.
Quando un oggetto di classe Wing esce dal campo di visibilit, ad esempio il flusso di
controllo del programma passa al di fuori di un blocco, viene invocato auomatcamente il
suo distruttore ~Wing(). La parola chiave delete usata nel codice del distruttore serve a
istruire una liberarazione di memoria dinamica. Essa deve essere usata insieme ad un puntatore allarea di memoria che si desidera rendere nuovamente disponibile. Nellesempio
precedente y il puntatore e listruzione delete[] y serve a dire al compilatore che la
memoria da liberare quella occupata dallintero array, dal primo allultimo elemento.
Tutto ci si verifica senza lintervento dellutilizzatore di Wing. Lutente semplicemente
crea e usa degli oggetti Wing come se fossero variabili di tipi predefiniti. Per esempio:
Wing wvar1("wing_config_1.xml"); // oggetto globale, costruito con dati
// letti da file
void f(){
Wing wvar2("wing_config_2.xml"); // oggetto locale
wvar1.Calculate();
wvar2.Calculate();
cout << "CL ala 1: " << wvar1.GetCL() << "\n"
<< "CL ala 2: " << wvar2.GetCL() << "\n";
} // qui viene invocato il distruttore di wvar2
cout << "CL ala 1: " << wvar1.GetCL() << "\n" // bene, wvar1 globale
<< "CL ala 2: " << wvar2.GetCL() << "\n"; // errore: wvar2 e
// out-of-scope

Appunti di programmazione a oggetti in C++ e Matlab

ver.2009.d Copyright A. De Marco

//---------------------------------------// File: Wing.cpp


//---------------------------------------// Headers
#include "Wing.h" // dichiara la classe Wing
// ...
//---------------------------------------// costruttori
Wing::Wing() { /* ... */ }
Wing::Wing(string filename) { /* ... */ }
//---------------------------------------// distruttore
Wing::~Wing() { /* ... */ }

Classi dichiarate in file


header e funzioni
membro definite in file di
implementazione

DRAFT

Per le variabili di tipo Wing valgono le stesse regole di nomi, visibilit, allocazione
vita, copia, e cos via, che valgono per i tipi predefiniti come int e char.
Si osservi che le funzioni membro come Init o loadGeometry, che sono solo dichiarate nel costrutto class, devono anchesse essere definite da qualche parte. La definizione
qui sinomino di implementazione. La dichiarazione di una (interfaccia di una) classe
come Wing quella vista sopra viene posta in un file apposito detto header, di estensione .h (o anche .hpp e .hxx). Il nome dellheader (esclusa lestensione) coincide in
genere con il nome della classe. La classe Wing avr un header file di nome Wing.h ed un
file di implementazione di nome Wing.cpp. Guardiamo un frammento di possibile file di
implementazione della classe Wing:

34

Capitolo 1 Tecniche di programmazione supportate dal C++

//---------------------------------------bool Wing::Init(string filename)


{
// effettua delle inizializzazioni preliminari
// ...
return loadGeometry(filename);
}
//---------------------------------------bool Wing::loadGeometry(string filename)
{
// cerca di caricare i parametri geometrici leggendoli
// dalle direttive contenute in un file di configurazione
// in formato XML (eXtended Markup Language)
// ...
return hesito della lettura filei;
}
//---------------------------------------Wing::Calculate() { /* ... */ }
// ...

Nella logica dei programmi organizzati in pi file sorgente compilati separatamente,


anche il codice utente di Wing dovr includere lheader che dichiara la classe. Ecco un
esempio:

DRAFT

ver.2009.d Copyright A. De Marco

// File: main.cpp
// Headers
#include "Wing.h" // dichiara la classe Wing
#include <iostream>
using namespace std;
// ...
int main()
{
Wing wing, htail;
if ( (!wing.Init("finite_wing_1.xml"))
||(!htail.Init("small_wing_2.xml")) ) {
cout << "Unable to configure wings from file." << endl;
PrintHelp();
exit(-1);
}
// da qui normale esecuzione
wing.SetAlphaDeg(3.0);
wing.SetMach(0.5);
wing.Calculate();
cout << "CD = " << wing.GetCD() << endl
<< "CL = " << wing.GetCL() << endl
<< "Cm = " << wing.GetCm() << endl;
// ...
return 0;
}

Valori di ritorno di
funzioni membro

Allinterno della funzione main vengono dichiarati due oggetti, wing e htail, di classe
Wing, inizializzati ed usati.
La funzione Init un esempio di metodo pubblico che fornisce un valore di ritorno
non void. Tale valore previsto dal progettista della classe in modo che venga utile agli
utenti di Wing. Infatti Init ritorna un valore logico che informa il codice chiamante sulA. De Marco

1.4 Astrazione dei dati

35

lesito dellinizializzazione avvenuta tramite caricamento dei dati da file. Nella funzione
main su riportata, il significato della negazione !htail.Init("small_wing_2.xml") usata
nellespressione logica del costrutto if potrebbe non essere immediato per i programmatori meno esperti. Un codice pi prolisso, ma non meno efficiente, potrebbe essere il
seguente:
Wing wing; // inizializzazione di default
Wing htail;
bool ok1 = wing.Init("finite_wing_1.xml"); // configura da file
bool ok2 = htail.Init("small_wing_2.xml"); // configura da file
if ( !ok1 || !ok2 ) {
// OR logico
cout << "Unable to configure from file." << endl;
exit(-1);
}

Tipi astratti

Nei tipi concreti come la classe Wing la rappresentazione dei dati non disaccoppiata
dallinterfaccia esterna della classe, ma piuttosto parte di quanto verrebbe incluso in un
frammento di programma che facesse uso di Wing. Pur essendo privata, e quindi accesAppunti di programmazione a oggetti in C++ e Matlab

Nomi di classi definite in


progetti di grandi
dimensioni

ver.2009.d Copyright A. De Marco

1.4.3

Consuetudini nella
scelta dei nomi di classi
e di oggetti

DRAFT

Si assegnano alle variabili booleane ok1 ed ok2 i valori ritornati da Init invocate attraverso gli oggetti wing e htail. Queste variabili vengono usate per controllare lesito delle
operazione di configurazione da file ed eventualmente interrompere il programma con un
comando exit.
Secondo una buona pratica della programmazione in C++, gli oggetti hanno nomi che
iniziano con una lettera minuscola ad esempio wvar, o anche wing mentre i
nomi delle classi di appartenenza hanno iniziali maiuscole. A volte, scorrendo il codice o
la documentazione di qualche libreria si possono trovare classi con nomi che iniziano per
C. Ad esempio pu capitare di incontrare il tipo non predefinito CString che secondo
una consuetudine dei programmatori di intefacce grafiche in ambiente Windows serve a
segnalare che CString una classe e non una semplice struttura dati (la struct del C) o
altro ancora. La consuetudine che bene seguire resta comunque quella di dare alle classi
dei nomi con iniziali maiuscole.
Una ulteriore consuetudine, usata soprattutto per progetti software di grandi dimensioni, quella di anteporre al nome naturale di una classe una sigla che indichi inequivocabilmente il nome del progetto. Ad esempio, nel codice sorgente del software di
simulazione di volo FlightGear [12] si trova la definizione della classe FGColumnVector3.
Le iniziali FG fanno capire a chi ispeziona il codice che si tratta di una delle classi definite nellambito del progetto e non una classe che appartiene ad una libreria da cui esso
eventualmente dipende. Il resto del nome ColumnVector3 fa capire che si tratta di una
classe di utilit che serve a gestire i punti dello spazio tridimensionale, espressi come
matrici colonna (3  1). Analogamente si definiscono le classi FGMatrix33 per le matrici
(3  3) ed FGQuaternion per la gestine del quaternione dellorientamento. Si rimanda il
lettore alla consultazione del sito internet di ispezione interattiva del codice sorgente di
FlightGear Flight Simulator [13].
Tipi come Wing sono chiamati tipi concreti, in contrapposizione ai tipi astratti, nei
quali linterfaccia isola in maniera ancora pi completa lutente dai dettagli implementativi.

36

Capitolo 1 Tecniche di programmazione supportate dal C++

sibile solo attraverso le funzioni membro, la rappresentazione comunque presente nella


dichiarazione della classe (ovvero nel file Wing.h incluso nel codice utente dalla direttiva
#include). Se tale rappresentazione cambiasse in maniera significativa (aggiunta di variabili membro, manutenzione dellimplementazione, eccetera), lutente sarebbe costretto a
ricompilare i file sorgenti della classe. Questo il prezzo da pagare per avere tipi concreti
che si comportano esattamente come quelli predefiniti.
Per i tipi poco soggetti a modifiche, e per i quali lutilizzo tramite variabili locali fornisce una maggior chiarezza ed efficienza, ci accettabile e spesso ideale. Tipicamente,
i programmatori non interessati a sviluppare librerie o progetti di ampio respiro possono
disegnare il loro software facendo uso per lo pi di classi concrete, delle librerie standard
ed eventualmente di librerie esterne.
Tuttavia il C++ offre agli sviluppatori di software la possibilit di mantenere completamente isolati gli utilizzatori di una classe dalle eventuali modifiche della sua implementazione. Ma per fare questo una classe come Wing definita nel paragrafo precedente
risulta insufficiente. Per raggiungere lo scopo bisogna disaccoppiare linterfaccia dalla rappresentazione e abbandonare luso di autentiche variabili locali (variabili membro
private).
Ridefiniamo una classe Wing come pura interfaccia (detta anche classe astratta):
class Wing {
public:
virtual bool Init(string filename) = 0;
virtual void Calculate(void) = 0;
virtual double GetCD(void) = 0;
virtual double GetCL(void) = 0;
virtual double GetCm(void) = 0;
class BadFormatFile { /* ... */ }; // usata come eccezione
};

DRAFT

ver.2009.d Copyright A. De Marco

Funzioni membro virtuali


pure e classi interfaccia

Questa una dichiarazione di classe molto semplice e molto particolare. La parola


virtual che precede una funzione membro significa pu essere ridefinita successivamente in una classe derivata da questa. Una classe derivata da unaltra praticamente
un tipo utente identico al primo ed eventualmente con qualche variabile membro e funzione membro in pi. Una classe derivata da Wing dellultimo esempio una classe che,
in pratica, fornisce limplementazione per linterfaccia Wing. La curiosa sintassi =0 indica
che una classe derivata da Wing deve necessariamente definire quella funzione (che si dice
funzione virtuale pura). Di conseguenza, questo tipo Wing pu fungere da interfaccia per
ogni classe che implementi le sue funzioni Init, Calculate, GetCD, GetCL e GetCm.
Un simile tipo Wing pu essere usato come segue:
bool f(Wing *w, string fname)
{
if ( !w->Init(fname) ) return false;
w->Calculate();
return true;
};

Tipi polimorfi

Loperatore -> del C++ permette di accedere alle funzioni ed alle variabili membro di un
oggetto quando se ne ha a disposizione un puntatore (Wing *w dichiara w come variabile
di tipo puntatore a Wing). Si noti come la funzione f utilizzi linterfaccia di Wing ignorandone completamente i dettagli implementativi. Una classe che fornisce linterfaccia a
A. De Marco

1.4 Astrazione dei dati

37

una serie di altre classi spesso chiamata tipo polimorfo. In questo caso Wing presuppone
necessariamente che debbano esistere classi derivate da essa poich le sue funzioni membro sono funzioni virtuali pure. I programmatori C++ progettano classi come Wing con il
proposito di costruire una gerarchia di classi che derivano da questa.
Come intuibile, limplementazione in una classe derivata potrebbe consistere di tutto
ci che apparteneva alla classe concreta del paragrafo precedente ed stato invece omesso nellultima versione dellinterfaccia Wing. Se si ha in mente di risolvere problemi di
analisi aerodinamica allora la funzione Calculate dovr rappresentare quasi certamente
laspetto pi importante dellimplementazione. La funzione Init sar altrettanto importante e provveder a fornire allutente di ciascuna classe derivata la possibilit di creare
file di dati e di configurazione iniziale del modello aerodinamico.
Si esamini la figura 1.1 nella quale sono richiamati schematicamente due diversi modelli aerodinamici di unala finita. Quello rappresentato nella figura 1.1a dovuto alla
famosa Teoria della linea portante (Lifting-Line Theory, LLT) di Prandtl [38] e permette
di calcolare, con un certo livello di approssimazione, le caratteristiche aerodinamiche di
unala ad esempio, i coefficienti di resistenza indotta, CDi , di portanza, CL , e di momento di beccheggio, Cm per una corrente di assegnato angolo dattacco e numero
di Mach asintotico M1 .

Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

class Wing_LLT : public Wing { // Wing_LLT implementa Wing


public:
Wing_LLT();
~Wing_LLT();
bool Init(string filename);
void Calculate(void);
// double GetCD(void);
// double GetCL(void);
// double GetCm(void);
// ...
private:
// double referenceSurface, span, aspectRatio, taperRatio, leSweep,
//
dihedral, geomTwist, aeroTwist;
// double alpha, mach;
// double cL, cD, cm;
// double *y; // span-wise stations
double *gamma // span-wise vorticity distribution
// ...
bool loadGeometry(string);
bool loadProfileData(string); // nella LLT possibile usare dati
// ...
// sperimentali dei profili
};
// ...
void Wing_LLT::Calculate(void)
{
// calcolo delle caratteristiche alari secondo la
// teoria della linea portante
// ...
}
// ...

ver.2009.d Copyright A. De Marco

A questo punto si potrebbe definire il nuovo tipo:

38

Capitolo 1 Tecniche di programmazione supportate dal C++

vorticit`a
aderente

vorticit`a
libera

centroidi
i

i C1

punti di
controllo

V1

(a) Modello basato sulla Teoria della linea portante di Prandtl.

y
vorticit`a
aderente
centroidi
vorticit`a
libera

i C1

V1
(b) Modello basato sulla Teoria del reticolo di vortici aderenti ad anello (Vortex Lattice Method, VLM).

DRAFT

ver.2009.d Copyright A. De Marco

Figura 1.1 Due possibili modelli per due diverse implementazioni di classi derivate da Wing.

Nella dichiarazione class Wing_LLT : public Wing { } il :public pu essere letto


come derivato da, implementa oppure un sottotipo di. Perch una funzione
come la f definita in questo paragrafo possa usare unistanza di Wing ignorando completamente i dettagli implementativi, qualche altra funzione deve farsi carico della creazione
di un oggetto sul quale f possa operare:
void g() {
Wing_LLT wllt;
// inizializzazione di default
wllt.SetAlpha(2.0); // potrebbero anche essere funzioni
wllt.SetMach(0.3); // membro di Wing anziche di Wing_LLT
try {
if ( !f( &wllt, // indirizzo di wllt
"wing_with_LLT_subdivisions.xml" ) )
{
cout << "Unable to configure from file." << endl;
exit(-1);
}
cout << "CL = " << wllt.GetCL() << endl;
}
catch (Wing::BadFormatFile) { // gestore delleccezione
A. De Marco

1.4 Astrazione dei dati

39

// oops: errore di formato


cout << "Bad format in Wing (LLT) configuration file" << endl;
}
}

In questo caso la rappresentazione del tipo Wing_VLM basata su un diverso insieme di dati:
le variabili private y e gamma che nella classe Wing_LLT sono array monodimensionali e
rappresentano la discretizzazione dellala nel senso dellapertura e le intensit dei vortici
aderenti ad essa associate (figura 1.1a) sono adesso degli array bidimensionali poich
necessario gestire anche i nodi lungo la direzione della generica corda alare. Per questo
motivo a questa rappresentazione dei datisi aggiunto larray x. Per quanto riguarda
linizializzazione e la configurazione dallesterno, per Wing_VLM sar certamente diverso
il meccanismo di lettura da file della geometria del modello (funzione loadGeometry).
Questa classe derivata una diversa implementazione dellinterfaccia Wing e lindirizzo
di un oggetto di tipo Wing_VLM pu essere lecitamente passato come primo argomento
della funzione f. Si dice che la classe derivata Wing_VLM, cos come la Wing_LLT,
Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

class Wing_VLM : public Wing { // Wing_VLM implementa Wing


public:
Wing_VLM();
~Wing_VLM();
bool Init(string filename);
void Calculate(void);
// ...
private:
const int max_sw = 100; // span-wise
const int max_cw = 5;
// chord-wise
_
_
double x[max sw][max cw]; // chord-wise divisions
double y[max_sw][max_cw]; // span-wise stations
double gamma[max_sw][max_cw] // bound vorticity
// ...
bool loadGeometry(string);
// ...
};
// ...
void Wing_VLM::Calculate(void)
{
// calcolo delle caratteristiche alari com
// il Vortex Lattice Method
// ...
}
// ...

ver.2009.d Copyright A. De Marco

Questa funzione perfettamente lecita in C++ e mostra una delle caratteristiche pi


importanti del linguaggio. La f non sa niente di Wing_LLT, ma conosce solo la classe
interfaccia Wing; operer altrettanto bene per una diversa implementazione di Wing.
Quale potrebbe essere una implementazione alternativa? In Aerodinamica applicata
il modello della linea portante non lunico disponibile per effettuare studi ingegneristici
sulle propriet delle ali. Esiste, ad esempio, un metodo di calcolo numerico dei coefficienti aerodinamici di unala chiamato Vortex-Lattice Method (VLM) che basato sulla
Teoria dei vortici aderenti ad anello [39]. Una rappresentazione schematica di questo
modello alternativo riportata nella figura 1.1b. Ecco un esempio di implementazione
alternativa:

40

La classe derivata
una (is a) classe base

Capitolo 1 Tecniche di programmazione supportate dal C++

una (is a) Wing. Questultima detta anche classe base o sopraclasse (base class o
super-class) mentre quelle da essa derivate si dicono sottoclassi (subclasses). Lesempio
seguente mostra un possibile uso di un oggetto di tipo Wing_VLM:
void h() {
Wing_VLM wvlm;
// inizializzazione di default
wvlm.SetAlpha(2.0); // potrebbero anche essere funzioni
wvlm.SetMach(0.3); // membro di Wing anziche Wing_VLM
try {
if ( !f( &wvlm, // indirizzo di wvlm
"wing_with_VLM_subdivisions.xml" ) )
{
cout << "Unable to configure from file." << endl;
exit(-1);
}
cout << "CL = " << wvlm.GetCL() << endl;
}
catch (Wing::BadFormatFile) { // gestore delleccezione
// oops: errore di formato
cout << "Bad format in Wing (VLM) configuration file" << endl;
}
}

DRAFT

ver.2009.d Copyright A. De Marco

1.4.4

Funzioni virtuali

Come viene associata alla giusta definizione della funzione w->Init(fname), oppure della funzione w->Calculate(), nella f? Quando f viene invocata dalla funzione h, devono
essere eseguite Wing_VLM::Init e Wing_VLM::Calculate. Per ottenere questo tipo di associazioni, un oggetto di tipo Wing deve contenere informazioni che indichino la funzione
che deve essere invocata durante lesecuzione (at execution time, che ben diverso da
at compilation time). Una tecnica che viene adottata dai compilatori moderni quella di
convertire il nome di ciascuna funzione virtual in un indice allinterno di una tabella
di puntatori a funzione. Questa tabella nel gergo tecnico degli sviluppatori di compilatori viene normalmente chiamata tabella delle funzioni virtuali o, semplicemente vtbl.
Ogni classe che possieda funzioni virtuali possiede una propria vtbl che identifica tali
funzioni.
Le funzioni nella vtbl permettono un corretto utilizzo delloggetto anche quando la
sua dimensione e la struttura dei suoi dati siano sconosciuti al chiamante. Tutto ci che
il chiamante serve sapere dove si trovi la vtbl in una Wing e lindice utilizzato per ciascuna funzione virtuale. Questo meccanismo di chiamate virtuali viene implementato dai
compilatori in maniera cos efficiente da essere paragonabile al meccanismo di normale
chiamata di funzione. Il suo costo aggiuntivo in termini di spazio occupato in memoria
il puntatore (un intero) in ogni oggetto di una classe che contenga funzioni virtuali pi
una vtbl per ognuna di tali classi.
A. De Marco

1.5 Programmazione orientata agli oggetti

Un esempio: laerodinamica di un velivolo completo

Sebbene lastrazione dei dati cio quella tecnica che porta alla progettazione e alluso
di classi concrete sia fondamentale per un buon progetto, si deve osservare che un tipo
concreto definisce una sorta di scatola nera. Una volta che la scatola nera stata definita,
questa interagisce in maniera rigida con il resto del programma, nel senso che non
possibile adattarla a nuovi usi, se non modificando la sua definizione. Questa situazione
pu essere ideale ma pu anche dimostrarsi poco flessibile in una serie di casi applicativi.
Nella progettazione delle classi Wing, Wing_LLT e Wing_VLM del paragrafo precedente,
visto il problema che si intende risolvere, si potrebbe pensare a cosa accomuna veramente
le implementazioni delle due classi derivate e a cosa, invece, le distingue. Nella formulazione di molti problemi pratici lastrazione dei concetti pu portare effettivamente ad
individuare un insieme di propriet comuni al gruppo dei possibili oggetti che il programmatore intende mettere in gioco. Per ciascuno degli oggetti tipicamente si possono anche
distinguere delle propriet specifiche, non possedute dagli altri. La capacit di di fattorizzare le propriet comuni e di distinguere tra le propriet comuni e quelle specifiche
traendone vantaggio definisce la programmazione orientata agli oggetti. I linguaggi dotati di costrutti che permettono di esprimere e utilizzare questa distinzione supportano la
programmazione orientata agli oggetti, gli altri no. Il meccanismo dellereditariet uno
degli elementi fondamentali di questo stile di programmazione.
Per approfondire questo concetto rivolgiamo lattenzione ad un problema simile a
quello del calcolo aerodinamico delle ali dei velivoli ma, in un certo senso, pi generale.
Supponiamo che il programmatore debba progettare un sistema di calcolo delle forze e dei
momenti agenti sullintera configurazione aerodinamica di un aeromobile. I progettisti dei
velivoli affrontano questo argomento integrando e sintetizzando le conoscenze provenienti
dallAerodinamica applicata ai profili alari, alle ali finite isolate, ai corpi affusolati, alle
combinazioni ala-fusoliera, e cos via. Un esempio di configurazione aerodinamica di un
velivolo completo mostrato nella figura 1.2f.
Come sanno bene gli ingegneri aerospaziali, per una data condizione di volo (quota,
velocit, angolo dattacco, angolo di derapata, posizione effettiva dei comandi di volo, eccetera), la stima delle forze e dei momenti aerodinamici agenti sul velivolo un problema
tuttaltro che banale. Una schematizzazione ingegneristicamente accettabile dellazione
aerodinamica sulla configurazione del velivolo nella sua interezza quella di considerare questultima come una sovrapposizione di effetti (aerodynamic build-up). Lazione
totale la somma delle azioni aerodinamiche esercitate dai singoli elementi presi isolatamente (ala, fusoliera, piani di coda, gondole dei motori, eccetera); tipicamente, a questi
contributi isolati vanno aggiunti dei termini ulteriori: i cosiddetti effetti di interferenza
aerodinamica (dovuti al fatto che ciascun elemento aerodinamico posto nella stessa corrente aerodinamica ma in presenza degli altri). Le rappresentazioni schematiche mostrate
nella figura 1.2 danno unidea di questo principio.
In particolare, nella figura 1.2a evidenziato il ruolo prevalente dellala principale di
un velivolo di configurazione tradizionale nella genesi della portanza. La figura 1.2b schematizza unaltra situazione di particolare importanza nel progetto di un velivolo che data
dalla necessit di realizzare e mantenere stabilmente un equilibrio al beccheggio attraverAppunti di programmazione a oggetti in C++ e Matlab

Ereditariet

Il calcolo aerodinamico
di un velivolo completo,
possibile applicazione
della tecnica di
programmazione a
oggetti

ver.2009.d Copyright A. De Marco

1.5.1

Programmazione orientata agli oggetti

DRAFT

1.5

41

42

Capitolo 1 Tecniche di programmazione supportate dal C++

(a) isolated wing (W)

(b) wing-horizontal tail (WH)

(c) wing-body (WB)

(d) wing-body-vertical tail (WBV)

(e) wing-body-horizontal tail (WBH)

(f) Velivolo completo

Figura 1.2 Possibili sottoinsiemi della configurazione aerodinamica di un velivolo. Essa pu es-

DRAFT

ver.2009.d Copyright A. De Marco

sere concepita come la composizione di un certo numero di corpi aerodinamici. Ci pu essere


tradotto opportunamente a livello di astrazione dati.

Caratteristiche comuni
alle diverse componenti
di una configurazione
aerodinamica

so un piano di coda orizzontale. Nelle figure 1.2c, 1.2d e 1.2e sono rappresentate le parti
di configurazione che devono essere necessariamente considerate, ad esempio, per una
ragionevole stima del coefficiente di resistenza del velivolo, o anche per un raffinamento
del calcolo delle caratteristiche aerodinamiche connesse alla portanza e al momento di
beccheggio.
Anche per il calcolo delle caratteristiche latero-direzionali dei velivoli valgono considerazioni analoghe. In quel contesto importante stimare la forza aerodinamica laterale
e i momenti di rollio e imbardata in condizioni di volo non simmetriche e risulta determinante la successione di schematizzazioni: fusoliera o body (B), ala-fusoliera o wing-body
(WB), ala-fusoliera-piano verticale di coda o wing-body-vertical tail (WBV). In tal caso,
il piano verticale di coda si comporta dal punto di vista aerodinamico esattamente come
unala, solo che disposto diversamente nei confronti della corrente fluida.
Dalle considerazioni precedenti si pu cominciare a pensare che nella configurazione
di un velivolo coesistono elementi aerodinamici che, dal punto di vista delle teorie aerodinamiche, possiedono un certo numero di caratteristiche in comune. Ci vero sia
in termini dei parametri geometrici che ne permettono la definizione e la collocazione
in un dato sistema di riferimento globale sia in termini delle grandezze aerodinamiche
incognite che concorrono al calcolo dellazione aerodinamica totale agente sul velivolo.
A tal proposito si esamini la figura 1.3. La posizione dellala rispetto ad un sistema di
riferimento globale del velivolo, detto riferimento costruttivo fOc ; xc ; yc ; zc g (con origine
Oc da qualche parte nella zona prodiera) data dal punto AW (lapice dellala) e dallanA. De Marco

1.5 Programmazione orientata agli oggetti

43

yc
AW
bW

xc

A?W
zc

?
cW

iW

xc

lW
Figura 1.3 Tipica schematizzazione della posizione di unala rispetto al sistema di riferimento

Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

golo di calettamento iW . La definizione geometrica dellala potr essere rappresentata da


?
alcuni parametri significativi; ad esempio: cW
la corda di radice, bW lapertura, e cos
via.
Dal punto di vista dellastrazione dei concetti, la figura 1.4 simile a quella precedente. Essa schematizza il posizionamento nel riferimento costruttivo dellimpennaggio
orizzontale (horizontal tail, H) ed il suo dimensionamento con dei parametri del tutto analoghi a quelli utilizzati per lala. Ecco che si avranno, tra i possibili parametri geometrici
rappresentativi, la corda di radice cH? e lapertura bH .
La posizione ed i parametri geometrici di questi corpi aerodinamici pu essere schematizzata in maniera del tutto generale in modo da comprendere anche gli altri elementi
della configurazione del velivolo come la fusoliera, il piano verticale di coda, eccetera.
Basta definire:
 un riferimento locale al generico elemento, fO; ; ; g, la cui origine O data nel
riferimento costruttivo,
 tre dimensioni caratteristiche di ciascun elemento, .` ; ` ; ` /, una per ogni direzione coordinata,
 un orientamento rispetto alla terna costruttiva dato dalla terna di angoli di Eulero
. ; ; /.
I coefficienti aerodinamici di forza e di momento calcolati per ciascun corpo potranno
essere riferiti agli assi locali e trasformati, alloccorrenza, in modo da essere riferiti a
qualsiasi altra terna, ad esempio a quella degli assi costruttivi.
Nel caso dellala rappresentata nella figura 1.3, ad esempio, lorigine del riferimento
locale potr coincidere con il punto AW , lasse  sar allineato con la corda di radice, lasse

ver.2009.d Copyright A. De Marco

costruttivo di un velivolo fxc ; yc ; zc g. Il punto AW lapice dellala ed un buon candidato per


rappresentare il refCenter di AerodynamicBody. Langolo di calettamento iW coincider con il
valore della variabile membro incidenceSetting.

44

Capitolo 1 Tecniche di programmazione supportate dal C++

yc
AH
bH

xc

A?H
zc

cH?
iH
.< 0/

xc
lH
Figura 1.4 Posizione del piano di coda orizzontale rispetto al sistema di riferimento costruttivo di
un velivolo fxc ; yc ; zc g. Il punto AH lapice dellimpennaggio e langolo iH ne il calettamento
intorno allasse trasversale yc .

DRAFT

ver.2009.d Copyright A. De Marco

 sar normale al piano di simmetria, orientato positivamente verso la destra del pilota,
e infine lasse  sar orientato in modo tale da rendere la terna levogira. Inoltre, lunico
angolo di Eulero non nullo dellorientamento di tale terna rispetto agli assi costruttivi
  iW .
Lastrazione di questi concetti porta alla definizione di una classe base che potremmo
chiamare AerodynamicBody:
class AerodynamicBody {
public:
AerodynamicBody();
virtual bool Init(string filename) = 0;
virtual void Calculate(void) = 0;
Point Where() { return refCenter; } // class Point defined elsewhere
virtual void Move(Point p, bool calc = true) {
refCenter = p;
/* ... */
if (calc) Calculate();
}
virtual void Orient(float a, float b, float c, bool calc = true) {
psi = a; theta = b; phi = c;
/* ... */
if (calc) Calculate();
}
virtual void SetAlphaDeg(float a, bool calc = true) {
alpha = a*degtorad;
/* ... */
if (calc) Calculate();
A. De Marco

1.5 Programmazione orientata agli oggetti

45

Un utente potrebbe voler cambiare la posizione e lorientamento del corpo aerodinamico rispetto al riferimento costruttivo. Per questo scopo la classe AerodynamicBody mette
a disposizione le funzioni: Where(), che ritorna un oggetto di classe Point contenente lorigine corrente del riferimento locale, Move(), che sposta lorigine in un punto desiderato,
e Orient() che ridefinisce lorientamento. La chiamata di ciascuna di queste funzioni
presuppone una chiamata alla funzione virtuale Calculate(). Ci sembra ragionevole,
dal momento che una qualunque variazione di posizionamento del corpo aerodinamico
rispetto alla corrente fluida e agli altri eventuali corpi aerodinamici compresenti dovrebbe
provocare una variazione dei coefficienti aerodinamici. Un ragionamento analogo vale
per SetAlphaDeg() e SetBetaDeg(), funzioni che consentono al programmatore di imAppunti di programmazione a oggetti in C++ e Matlab

DRAFT

Si dice che la classe AerodynamicBody raggruppa, fattorizza, mette a fattor comune,


le propriet e le funzionalit che ci si aspetta da un generico corpo aerodinamico. Di un
corpo aerodinamico si vogliono conoscere, ad esempio, i coefficienti di forza e di mo
mento CF ; CF ; : : : ; CM rispetto al proprio riferimento locale oppure i corrispondenti

CFx ; CFy ; : : : ; CMz rispetto ad un riferimento globale.

ver.2009.d Copyright A. De Marco

}
virtual void SetBetaDeg(float b, bool calc = true) {
beta = b*degtorad;
/* ... */
if (calc) Calculate();
}
// ...
float GetCFcsi() { return cFcsi; } // w.r.t. local ref.
float GetCFeta() { return cFeta; }
// ...
float GetCMzet() { return cMzet; }
float GetCFx();
// w.r.t. global ref
float GetCFy();
// ...
float GetCMz();
// ...
class BadFormatFile { /* ... */ }; // used as ecception class
// ...
protected:
static const double radtodeg; // these constants are unique
static const double degtorad; // for all objects
// ...
private:
Point refCenter; // origin of local ref.
float psi, theta, phi; // orientation w.r.t. global axes
float refLcsi, refLeta, refLzet; // reference quantities
float refSurface, refVolume;
float mach, reynolds;
float alpha, beta;
Point poleMoments; // moment ref. location
float cFcsi, cFeta, cFzet, cMcsi, cMeta, cMzet;
float cFx, cFy, cFz, cMx, cMy, cMz;
// ...
fromLocalToGlobal(); // transforming aerodynamic coefficients
// ...
};

46

Capitolo 1 Tecniche di programmazione supportate dal C++

postare nuovi angoli aerodinamici di funzionamento determinando una variazione delle


forze e dei momenti risultanti.
* Le definizioni di alcune funzioni membro di AerodynamicBody mostrano che in C++ possibile assegnare un valore di default agli argomenti di una funzione. Ad esempio la definizione virtual void
Move(Point p, bool calc = true) prevede un secondo argomento, calc, che di tipo bool. La sorta di
assegnazione che compare nella lista di argomenti vuol dire se il codice chiamante non specifica questo
argomento, assegna ad esso un valore true. La definizione di Move() consente allutente di modificare
una caratteristica delloggetto e di scegliere se eseguire o non eseguire la funzione Calculate(). Questa
logica valida anche per altre funzioni membro e potrebbe dimostrarsi molto utile per le classi derivate da
AerodynamicBody che ridefiniscono una funzione Calculate() particolarmente onerosa dal punto di vista
computazionale.

La rappresentazione della classe AerodynamicBody data dalle variabili definite nella


parte privata, tra le quali si riconosceranno lorigine refCenter del riferimento locale
ed i suoi angoli di Eulero psi, theta e psi. Tra le altre variabili che costituiscono la
rappresentazione, vi sono le importanti grandezze cFcsi, cFeta, : : :, cMzeta, cFx, cFy,
: : :, cFz. Esse sono destinate a contenere i valori dei coefficienti aerodinamici.

DRAFT

ver.2009.d Copyright A. De Marco

1.5.2

Dalle classi concrete alle gerarchie di classi

La classe AerodynamicBody non direttamente usabile dal programatore poich contiene


le funzioni virtuali pure Calculate() e Init(). Questo significa che bisogna definire una
classe che erediti da AerodynamicBody sia la sua rappresentazione che la sua interfaccia,
cio lorganizzazione dei dati e le funzioni previste per manipolarli.
Le funzioni dichiarate virtual nella classe base AerodynamicBody sono quelle che
possono avere una definizione specifica per ciascun tipo di corpo aerodinamico che il
programmatore sia disposto a concepire. Tra queste vi certamente Calculate(), che
deve basarsi su tecniche di calcolo diverse a seconda se si tratti di un corpo affusolato,
come una fusoliera o una gondola motore, o di una superficie portante principale, come
lala. Analogamente, la funzione di interfaccia Init() trover una definizione specifica
per ciascun tipo di forma aerodinamica, dovendo leggere da un file un insieme opportuno
di direttive di configurazione.
Si consideri ad esempio la fusoliera rappresentata nella figura 1.5. Il C++ permette di
definire una classe concreta Fuselage derivandola dalla classe astratta AerodynamicBody.
Questultima la classe base. Da essa la classe Fuselage, la classe derivata, eredita linterfaccia e la rappresentazione. In un apposito file di intestazione, ad esempio Fuselage.h,
si potr dichiarare:
class Fuselage : public AerodynamicBody {
public:
Fuselage();
bool Init(string filename);
void Calculate(void); // to be defined in the implementation
private:
vector<float> vCsiSectionC,
vZetSectionC,
vCrossSectionArea;
// ...
};

Altrove, ad esempio nel file di implementazione Fuselage.cpp, si potranno definire le


funzioni:
A. De Marco

1.5 Programmazione orientata agli oggetti

xc

lB
zc

47

bf .x/
bf,max  bB
hf .x/

yc

hf,max  hB

B
V1
Figura 1.5 I principali parametri geometrici ed aerodinamici di una fusoliera (Body, B). Tipi-

camente, questo corpo aerodinamico ha un riferimento locale coincidente con quello globale:
.O; ; ; /  .Oc ; xc ; yc ; zc /.

#include "Fuselage.h"
// ...
Fuselage fus;
if ( !fus.Init("a380_fuselage.xml") ) {
cout << "Error! Could not load config file for Fuselage object.\n"
exit(-1);
}
fus.SetMach(0.5,false);
// Aerodynamic
fus.SetAlphaDeg(2,false); // settings
fus.SetBetaDeg(0,false);
fus.Calculate();
// Aerodynamic calculation
cout << "Cx = " << fus.GetCFx << endl;
Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

La rappresentazione interna degli oggetti di tipo Fuselage quella ereditata dalla


classe AerodynamicBody. In pi, la classe derivata si specializza dotandosi nella parte privata di tre contenitori di tipo vector<float>, le variabili vCsiSectionC, vZetSectionC e
vCrossSectionArea (vettori della libreria standard), ed implementando le proprie versioni
di Init() e Calculate().
Il tipo Fuselage pu essere istanziato e ciascun oggetto potr effettivamente interagire
con le risorse di sistema:

ver.2009.d Copyright A. De Marco

void Fuselage::Init(string filename) {


/* ... */
}
void Fuselage::Calculate(void) {
/* ... */
}

48

Capitolo 1 Tecniche di programmazione supportate dal C++

Il passaggio dalla classe base AerodynamicBody alla classe concreta Fuselage un


esempio molto semplice di gerarchia di classi. A seconda del problema che si intende
modellare, esiste la possibilit di articolare a piacimento la catena di derivazione di nuove
classi, a partire da superclassi a loro volta derivate da altre. Lo scopo quello di costruire
dei meccanismi che consentano la massima flessibilit nelluso degli oggetti, separando
per quanto sia possibile limplementazione delle funzionalit e la rappresentazione degli
oggetti dalle loro interfacce. Questo in effetti ci che si intende per programmazione
orientata agli oggetti. Il paradigma diventa:
Decidi quali classi ti occorrano;
fornisci un insieme completo di operazioni per ogni classe;
rendi esplicite le parti comuni mediante lereditariet.

DRAFT

ver.2009.d Copyright A. De Marco

La programmazione a
oggetti ideale per
sviluppare delle GUI

Complessit intrinseca
del codice sorgente di
applicazioni basate su
GUI

Programmi eseguiti da
console

La classe Fuselage pu essere, a sua volta, la classe base di una nuova classe, che eredita dalla prima la rappresentazione e linterfaccia e si specializza per qualche motivo. Ad
esempio, per una particolare tecnica di calcolo dei coefficienti aerodinamici oppure perch modella una particolare categoria di fusoliere che hanno delle caratteristiche peculiari
e non fattorizzabili negli oggetti aerodinamici generici modellati da AerodynamicBody o
da Fuselage.
A questo punto, dalla classe base AerodynamicBody si pu pensare di derivare classi
analoghe a quella che modella fusoliere, ramificando la gerarchia. naturale derivare
una classe Wing e da essa derivarne eventualmente delle altre, ad esempio HTail e Canard
che modellano tipi particolari di superfici portanti le cui caratteristiche aerodinamiche
vanno valutate in modo specifico. Questo discorso porta alla costruzione di una gerarchia
di tipi che permette di istanziare oggetti dalle propriet adeguate agli oggetti fisici che
essi rappresentano. Gli oggetti interagiranno adeguatamente tra di loro e con le risorse di
sistema.
Un campo applicativo in cui la programmazione orientata agli oggetti si dimostra
particolarmente adatta quello dello sviluppo di applicazioni dotate di interfacce grafiche
(Graphical User Interfaces, GUI). Ma non il solo. Esistono svariate classi di problemi
di tipo ingegneristico che ben si prestano alla modellazione per classi di oggetti. Come si
visto, il calcolo aerodinamico dei velivoli ne un esempio.
Come sostiene Herbert Schildt nella sua popolare Guida completa [4], il C++ il
linguaggio perfetto per Windows ed il modo in cui supporta la programmazione a oggetti
completamente soddisfacente per sviluppare applicazioni in tale ambiente operativo (si
potrebbe aggiungere che ci vale anche per Mac OS X e per i vari ambienti interattivi
di Linux). Daltra parte, i programmi per Windows sono, per loro stessa natura, estesi e
complessi. La quantit di codice necessario per creare anche solo la semplice struttura
di un programma per Windows dotato di una seppur minimale interfaccia grafica
occupa dalle 50 alle 70 righe. Per scrivere un programma per Windows che sia utile
per illustrare le funzionalit del C++ sono necessarie centinaia di righe di codice. In
altre parole, Windows non lambiente pi appropriato per descrivere le funzionalit
di un linguaggio di programmazione. Naturalmente possibile utilizzare un compilatore
Windows per compilare semplici applicazioni che stampino messaggi sul canale di output
standard ed eventualmente accettino un input da tastiera. In questi casi il compilatore
creer unapplicazione in grado di lanciare automaticamente una sessione a console nella
quale eseguire il programma.
A. De Marco

1.6 Cosa non abbiamo detto?

Cosa non abbiamo detto?

std::vector <float> v;
v.push_back(1.0);
v.push_back(-2.5);
v.push_back(5.0);
for (int i = 0; i < v.size(); i++)
std::cout << v[i] << std::endl;

Sulla base degli esempi precedenti, si lascia al lettore lesercizio di ragionare sul
seguente frammento di codice:
Appunti di programmazione a oggetti in C++ e Matlab

Programmazione
generica e Standard
Template Library, STL

Il contenitore vector
della STL

ver.2009.d Copyright A. De Marco

Non abbiamo parlato delle classi template del C++ e della programmazione generica, e
non ne parleremo approfonditamente. Questa tecnica di programmazione alla base della
Standard Template Library (STL), una libreria software inclusa nelle implementazioni
standard del linguaggio C++. La commissione dellente ISO (International Organization
for Standardization) che ha fissato ufficialmente le caratteristiche del C++ ha stabilito
che ogni compilatore che si dica standard compliant deve essere dotato di librerie che
implementano le funzionalit della STL, tra le quali la possibilit di usare strutture dati
generiche, iteratori e algoritmi generici. Un ottimo testo che approfondisce gli aspetti
progettuali e le modalit duso della STL quello di Vandevoorde e Josuttis [8]. Non tutti
i programmatori sono tenuti a comprendere nei minimi dettagli le tecniche che consentono
di creare una libreria come la STL. Per molti sufficiente capire quanto basta per poter
usare le classi che essa offre.
La STL costituisce uno strato software fondamentale per chiunque voglia lavorare
produttivamente con il C++. Essa fornisce un insieme precostituito di classi che il programmatore si aspetta di usare nella risoluzione della maggior parte dei problemi. Esempi
di classi del genere sono i container e gli array associativi, che nella STL sono definiti
in modo da poter operare con qualsiasi tipo di dato, sia primitivo che definito dallutente. Nelle classi container a proposito di funzionalit che un programmatore si aspetta
di trovare gi pronte alluso vi sono gi implementate con criteri unificati le funzioni standard di copia, assegnamento, inserimento/rimozione, iterazione tra gli elementi
(scorrimento).
Gli sviluppatori pi avanzati possono anche derivare classi personalizzate da quelle
presenti nella STL purch essi rispettino pochi vincoli (ad esempio, limplementazione di
operatori o funzioni di assegnamento o confronto). In tal senso la STL, unitamente alla
potenza espressiva di un linguaggio come il C++, offrono la possibilit di creare nuove
classi e librerie complete di tutte le funzioni e operazioni elementari.
Il container vector una delle classi pi importanti messe a disposizione dalla STL.
Un vector un contenitore di elementi omogenei simile allarray, con la funzione di
permettere laccesso ai suoi contenuti in modo rapido e ottimizzato, utilizzando indici o
tramite un iteratore. La definizione di questa classe si trova nel file header <vector> del
namespace std. Esso rappresenta in sostanza una versione evoluta dellarray del C. Infatti,
al contrario di un array tradizionale, un vector non ha una capacit massima prefissata in
tempo di compilazione, ma si espande durante lesecuzione a seconda delle necessit.
Laccesso agli elementi di un vector di tipo casuale, e particolarmente efficaci sono
le operazioni di inserimento (push_back()) e rimozione in coda (pop_back()). Ecco un
esempio di costruzione e scorrimento di un vector di float:

DRAFT

1.6

49

50

Capitolo 1 Tecniche di programmazione supportate dal C++

std::vector <AerodynamicBody *> v;

// a vector of pointers

Fuselage * pF = new Fuselage();


// put in heap
pF->Init("fuselage.xml");
// define from file
v.push_back((AerodynamicBody *)pF); // cast and put in vector
Wing * pW = new Wing();
pW->Init("wing.xml");
v.push_back((AerodynamicBody *)pW); // cast and put in vector
HTail * pH = new HTail();
pH->Init("horizontal_tail.xml");
v.push_back((AerodynamicBody *)pH); // cast and put in vector
double Cx = 0.0;
for (int i = 0; i < v.size(); i++) {
v[i]->Calculate();
// call the correct member function
Cx += v[i]->GetCFx(); // cumulate the coefficient Cx
}
std::cout << "Aircraft total Cx = " << Cx << std::endl;

Nel prossimo capitolo continueremo ad approfondire le peculiarit della programmazione a oggetti attraverso ulteriori esempi in linguaggio C++. Questo servir anche allo
scopo di introdurre altri dettagli sulle caratteristiche stesse del linguaggio. A tal proposito
vale la pena far precedere il prossimo argomento da un aforisma.
Il C++ sarebbe un liguaggio decente per insegnare la programmazione, se
potessimo insegnare la parte ++ senza dover spiegare anche la parte C.

DRAFT

ver.2009.d Copyright A. De Marco

Michael B. Feldman

A. De Marco

Capitolo 2
Ancora sulla programmazione in C++
If you think C++ is not overly complicated, just what is a protected
abstract virtual base pure virtual private destructor and when was the
last time you needed one?
Tom Cargill

Indice
.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

52
59
62
62
63
66

[capitolo incompleto]
Il C++ fu inventato nel 1980 dal ricercatore informatico danese Bjarne Stroustrup,
che ricav concetti gi presenti in precedenti linguaggi (come il Simula67) per produrre
una versione modificata del C, che chiam: C con le classi. Il nuovo linguaggio univa
la potenza e lefficienza del C con la novit concettuale della programmazione a oggetti, allora ancora in stato embrionale (cerano gi le classi e leredit, ma mancavano
loverload, le funzioni virtuali, i riferimenti, i template, la libreria e moltre altre cose).
Il nome C++ fu introdotto per la prima volta nel 1983, per suggerire la sua natura
evolutiva dal C, nel quale ++ loperatore di incremento (taluni volevano chiamarlo D,
ma C++ prevalse, per i motivi detti).
Allinizio, comunque, e per vari anni, il C++ rest un esercizio quasi privato del suo
autore e dei suoi collaboratori, progettato e portato avanti, come egli stesso disse, per
rendere pi facile e piacevole la scrittura di buoni programmi.
Tuttavia, alla fine degli anni 80, risult chiaro che sempre pi persone apprezzavano ed
utilizzavano il linguaggio e che la sua standardizzazione formale era un obiettivo da perseguire. Nel 1990 si form un comitato per la standardizzazione del C++, cui ovviamente
partecip lo stesso Autore. Da allora in poi, il comitato, nelle sue varie articolazioni,
divenne il luogo deputato allevoluzione e al raffinamento del linguaggio.
Finalmente lapprovazione formale dello standard si ebbe alla fine del 1997. In questi ultimi anni il C++ si ulteriormente evoluto, soprattutto per quello che riguarda

ver.2009.d Copyright A. De Marco

Elementi di base . . . . . . . . . .
Il mondo delle classi e degli oggetti
La libreria standard . . . . . . . .
Le librerie Boost . . . . . . . . . .
Esempi . . . . . . . . . . . . . . .
Come imparare il C++ . . . . . . .

DRAFT

2.1
2.2
2.3
2.4
2.5
2.6

Capitolo 2 Ancora sulla programmazione in C++

52

limplementazione di nuove classi nella libreria standard.


Per descriverlo in sintesi si pu affermare che il C++ un linguaggio di programmazione general-purpose, orientato alla realizzazione di sistemi che:
 pu dirsi un C migliorato,
 supporta lastrazione dei dati,
 supporta la programmazione agli oggetti,
 supporta la programmazione generica.

2.1
Case sensitivity

Moduli funzione

Il linguaggio C++ (come il C) distingue i caratteri maiuscoli da quelli minuscoli ( un


linguaggio case sensitive). Ad esempio, i nomi MiaVariabile e miavariabile indicano
due variabili diverse.
In C++ (come in C) ogni modulo di programma una funzione. Non esistono subroutines o altri tipi di sottoprogramma. Ogni funzione identificata da un nome. Le
procedure possono essere simulate definendo funzioni che ritornano un void e gestendo
opportunamente gli argomenti attraverso variabili di tipo puntatore.

2.1.1
La funzione main

Passaggio di parametri
da riga di comando e
codici di terminazione
dei programmi

Elementi di base

Interfaccia dei programmi con il sistema operativo

Quando si manda in esecuzione un programma, questo inizia sempre dalla funzione identificata dalla parola chiave main. Questa funzione speciale viene detta in gergo entry
point del programma ed invocata dal sistema operativo, che gli pu passare dei parametri; a sua volta main pu restituire al sistema un numero intero. Questo valore coincide
con il valore di ritorno di main e di solito destinato ad essere analizzato a valle dellesecuzione del programma come possibile codice di errore (detto in gergo return status o
exit code). Ad esempio, lesecuzione da console del programma convert.exe pu essere
invocata dal prompt dei comandi (>>) in questo modo:

DRAFT

ver.2009.d Copyright A. De Marco

>> convert.exe drawing.eps --output-format=PDF

hinvioi

La console attraverso la quale viene eseguito questo comando pu essere: il prompt dei
comandi di Windows (anche noto come prompt dei comandi DOS), una tipica finestra
shell di Linux oppure una finestra dei comandi Cygwin (che rappresenta il porting su
piattaforme Windows di gran parte delle funzionalit di Linux [24]). Se la console
dotata di interprete Bash [20], lexit code ottenibile attraverso il comando:
>> echo $?
>> 0

hinvioi

La particella $? , tecnicamente, una macro destinata ad essere espansa (cio trasformata in una stringa) e serve ad interrogare linterprete dei comandi sullo stato di ritorno
dellultimo comando. Questo valore passato al comando echo di Bash che lo stampa sul
prompt. Il valore 0 restituito in questo caso segnala che la funzione main implementata nel
codice eseguibile di convert.exe ha fornito un valore di ritorno nullo. Il programmatore
che ha creato questo eseguibile ha probablimente seguito la regola per cui un valore di
ritorno 0 corrisponde ad una condizione di terminazione normale dei programmi.
La modalit dinvocazione del programma convert.exe fa luce, a sua volta, sul perch la funzione main dei programmi C e C++ accetta degli argomenti. Dopo aver premuto
A. De Marco

2.1 Elementi di base

53

il tasto di invio, linterprete dei comandi (sia esso quello di Bash o del DOS) esegue
lanalisi della stringa di caratteri costituita dallintera riga di comando (command line
parsing). Esso riesce a separarla (interpretando gli spazi bianchi come separatori) in tre
stringhe: convert.exe, drawing.eps e --output-format=PDF; queste stringhe, incluso il nome stesso del programma che si esegue, vengono dette parametri della riga di
comando (command line parameters). La funzione main detta entry point del programma convert.exe proprio perch, quando questo viene mandato in esecuzione, il sistema
tenter di comunicarle sempre due valori: il numero di parametri della riga di comando
ed un puntatore ad un array di stringhe in stile C (sequenza di costanti di tipo carattere
terminate dal carattere di escape \0, detto null terminator).
Ecco svelato il motivo per cui le funzioni main dei programmi C e C++ sono tipicamente definite in questo modo:
int main(int argc, char *argv[]){

hcorpo della funzionei

La parte di riga su riportata che va dal primo carattere alla parentesi tonda chiusa
int main( ... ) viene detta in gergo function signature (la firma della funzione).
Essa dice che la funzione in esame ha per nome main, che ritorna un valore di tipo int
e che ha due argomenti: la variabile argc, di tipo int, e la variabile argv, di tipo array
di puntatori a caratteri (char*). Il valore di argc (che sta per argument count) proprio
il numero degli argomenti della riga di comando che servita ad invocare il programma.
Nel nostro esempio linterprete dei comandi passa a main un argc pari a 3 e un vettore
argv pari a:
{"convert.exe\0","drawing.eps\0","--output-format=PDF\0"}

in cui lelemento di posto 0, cio argv[0], sempre il nome delleseguibile. Nella firma di main il vettore argv ha dimensioni non specificate, cio automaticamente determinate a runtime (in fase di esecuzione) a seconda di come lutente invoca il programma
dal prompt. Se lo stesso programma deve essere usato per convertire limmagine vettoriale contenuta nel file drawing.eps in unimmagine bitmap, allora la riga di comando
dovrebbe essere un qualcosa di simile:
>> convert.exe drawing.eps --output-format=JPG --resolution-dpi=72

hinvioi

stato qui uno dei compiti del programmatore che ha implementato lalgoritmo di parsing della riga di comando quello di offrire allutente di convert.exe la possibilit di
utilizzare lapplicazione per eseguire questo tipo di conversione e di specificare la risoluzione del risultato. Il buon senso dice che probabilmente lultimo comando produce in
output un file drawing.jpg della risoluzione voluta.
Come si pu dedurre da questi esempi, il C++ si interfaccia al sistema operativo attraverso lentry point dei programmi esattamente come ci avverrebbe per applicazioni
scritte in linguaggio C. Ci dovuto al fatto che il C++ stato progettato anche per poter
inglobare il C come una sorta di sottolinguaggio. Questa caratteristica permette di poter
compilare gran parte del codice C esistente con un compilatori C++ (standard compliant)
e di ereditare le efficienti librerie di funzioni di sistema sviluppate negli anni.
Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

{"convert.exe\0","drawing.eps\0",
"--output-format=JPG\0","--resolution-dpi=72\0"}

ver.2009.d Copyright A. De Marco

con un argument count pari a 4 ed un vettore argv:

Capitolo 2 Ancora sulla programmazione in C++

54

2.1.2
Le tre parti di una
funzione

Le funzioni

La funzione main una funzione speciale ma, dal punto di vista formale, una funzione
C++ come tutte le altre. Delle funzioni C++ si distinguono tre caratteristiche principali:
la lista degli argomenti, il blocco delle sue istruzioni ed il tipo del valore di ritorno.
Gli argomenti della lista di parametri di scambio passati dal programma chiamante
vanno indicati fra parentesi tonde dopo il nome della funzione; f(void) indica che la
funzione di nome f non ha argomenti.
Il blocco di delle funzioni C++ lambito di azione, anche detto ambito di visibilit
o scope delle istruzioni della funzione; va racchiuso fra parentesi graffe. Ogni istruzione
deve terminare con ; (pu estendersi su pi righe o vi possono essere pi istruzioni
sulla stessa riga). Unistruzione costituita da una successione di token: un token il pi
piccolo elemento di codice individualmente riconosciuto dal compilatore. Sono token:
gli identificatori, le parole-chiave, le costanti letterali o numeriche, gli operatori e alcuni
caratteri di punteggiatura. Gli spazi bianchi e gli altri caratteri separatori (horizontal
or vertical tabs, new lines, formfeeds) fra un token e laltro o fra unistruzione e laltra,
sono ignorati. In assenza di separatori il compilatore analizza listruzione da sinistra a
destra e tende, nei casi di ambiguit, a separare il token pi lungo possibile. Ad esempio,
listruzione:
a = i+++j;

pu essere interpretata come:


a = i + ++j;

oppure come:
a = i++ + j;

DRAFT

ver.2009.d Copyright A. De Marco

Commenti

Secondo lo standard, il compilatore sceglie la seconda interpretazione.


Il tipo del valore di ritorno di una funzione al programma chiamante va indicato prima
del nome della funzione ed obbligatorio; se void indica che non c valore di ritorno.
I commenti sono brani di programma (che il compilatore ignora) inseriti al solo scopo
di documentazione, cio per spiegare il significato delle istruzioni e cos migliorare la
leggibilit del codice. Sono molto utili anche allo stesso autore, per ricordargli quello che
ha fatto, quando ha necessit di rivisitare il programma per esigenze di manutenzione o di
aggiornamento. Un buon programma si caratterizza anche per il fatto che fa abbondante
uso di commenti. In C++ ci sono due modi possibili di inserire i commenti. Nel primo,
ereditato dal linguaggio C, larea di commento introdotta dal doppio carattere /* e termina con il doppio carattere */ (pu anche estendersi su pi righe). In alternativa, larea
di commento inizia con il doppio carattere // e termina alla fine della riga.
Il programma seguente un esempio minimale di programma in C++:
#include <cstdio>
void main(void)
{
printf("Hello world!\n");
}

Lo stesso programma riportato con laggiunta di commenti che spiegano i diversi elementi formali previsti dal linguaggio:
A. De Marco

2.1 Elementi di base

55

#include <cstdio> /* inserisce il file che dichiara printf,


<...> indicano che il file cstdio va cercato
in una cartella di default del sistema
*/
void // nessun valore di ritorno
main(void) // nessun argomento accettato dallentry point
// non sono previste parametri da riga di comando
{ // inizio blocco
// printf, funzione di libreria standard
printf("Hello world!\n"); // stampa una stringa sul canale
// di output standard
} // fine blocco

Tipi nativi

Come noto, per tipo di una variabile si intende un termine di classificazione che raggruppa tutte quelle variabili che sono memorizzate nello stesso modo e a cui si applica lo
stesso insieme di operazioni. Nel linguaggio C++ viene esercitato (dal compilatore) un
forte controllo sui tipi (strong type checking), nel senso che in fase di compilazione viene
regolata e limitata la conversione da un tipo allaltro (casting) e controllata linterazione
fra variabili di tipo diverso.
In C++ esistono solo 5 tipi, detti intrinseci o nativi:
int numero intero di 2 o 4 byte,
char numero intero di 1 byte (interpretabile come codice ascii di un carattere),
float numero in virgola mobile con 6-7 cifre significative (4 byte),
double numero in virgola mobile con 15-16 cifre significative (8 byte),
bool valore booleano: true o false (1 byte).
In realt il numero di tipi possibili molto pi grande, sia perch ogni tipo nativo pu
essere specializzato mediante i qualificatori di tipo, sia perch il programma stesso pu
creare propri tipi personalizzati (detti tipi astratti).

Tipi nativi del C++

Il preprocessore e le unit di traduzione dei programmi

In C++ (come in C), prima che il compilatore inizi a lavorare, viene attivato un programma, detto preprocessore, che ricerca nel file sorgente speciali istruzioni, chiamate
direttive.
Una direttiva inizia sempre con il carattere # (nella colonna 1) e occupa una sola riga
(non ha un terminatore, in quanto finisce alla fine della riga; riconosce per i commenti,
introdotti da // o da /*, e la continuazione alla riga successiva, definita da \).
Il preprocessore crea una copia del file sorgente (da far leggere al compilatore) e, ogni
volta che incontra una direttiva, la esegue sostituendola con il risultato delloperazione.
Pertanto il preprocessore, eseguendo le direttive, non produce codice binario, ma codice
sorgente per il compilatore.
Ogni file sorgente, dopo la trasformazione operata dal preprocessore, prende il nome
di translation unit o unit di traduzione. Ogni translation unit viene poi compilata separatamente, con la creazione del corrispondente file oggetto, in codice binario. Spetta al
linker, infine, collegare tutti i files oggetto, generando un unico programma eseguibile.
Appunti di programmazione a oggetti in C++ e Matlab

Le direttive di
compilazione

Le translation unit

ver.2009.d Copyright A. De Marco

2.1.4

Il C++ effettua uno


stretto controllo sui tipi di
variabile

DRAFT

2.1.3

Capitolo 2 Ancora sulla programmazione in C++

56

La direttiva #include

Nel linguaggio C++ esistono molte direttive (alcune delle quali dipendono dal sistema operativo). Le pi usate sono le seguenti: #include, #define, #undef e direttive
condizionali.
La direttiva #include molto importante. Ad esempio la sequenza:
#include <filename1>
#include "filename2"

La direttiva #define di
una costante

determina linserimento, nel punto in cui si trova la direttiva, dellintero contenuto dei
file filename1 e filename2. Luso delle parentesi angolari, intende che filename1 vada
cercato nel percorso (directory) di default del linguaggio (opportunamente preimpostato
o comunicato al compilatore al momento della compilazione). Se invece si usano le virgolette, il file, per essere incluso con seuccesso, deve trovarsi nella stessa directory in cui
risiede il sorgente del programma.
La direttiva #include viene usata quasi esclusivamente per inserire gli header-files
(.h) ed particolarmente utile quando in uno stesso programma ci sono pi file sorgenti
(implementation-files) che includono lo stesso header-file.
Quando il preprocessore incontra la seguente direttiva:
#define

hidentificatorei hvalorei

dove, hidentificatorei un nome simbolico (che segue le regole generali di specifica di


tutti gli altri identificatori) e hvalorei unespressione qualsiasi, delimitata a sinistra da
spazi bianchi (blank, ), caratteri di tabulazione (tab, \t) e a destra da dei blanks, dei
tab o dal carattere di andata a capo (new-line, \n), lhidentificatorei viene sostituito con
hvalorei in tutto il file (da quel punto in poi). In generale la direttiva #define serve per
assegnare un nome a una costante (che viene detta costante predefinita). Ad esempio,
la direttiva:

DRAFT

ver.2009.d Copyright A. De Marco

#define M_PI 3.14159265358979323846

sostituisce (da quel punto in poi) in tutto il file la parola M_PI con 3.14159265358979323846.
Ogni volta che il programma deve usare il valore numerico che approssima , si pu
specificare in sua vece la costante M_PI.
Va osservato che che la sostituzione assolutamente fedele e cieca, qualunque sia il
contenuto dellespressione che viene sostituita allidentificatore. Il compito del preprocessore quello di effettuare sostituzioni di questo tipo, di includere file esterni e selezionare le parti di codice sorgente da compilare (escludendone eventualmente delle altre). il
compito di segnalare gli errori viene lasciato al compilatore.
Esistono principalmente due vantaggi nelluso di #define. In primo luogo, se il programmatore decide di cambiare valore a una costante, sufficiente che lo faccia in un
solo punto del programma. In secondo luogo, molto spesso i nomi sono pi significativi e
mnemonici dei numeri (oppure pi brevi delle stringhe, se rappresentano costanti stringa)
e perci luso delle costanti predefinite permette una maggiore leggibilit del codice e una
maggiore efficienza nella programmazione.
In pratica la direttiva #define produce gli stessi risultati dello specificatore di tipo
const, che una parola chiave peculiare del C++. Al posto della direttiva dellesempio
precedente si sarebbe potuto scrivere la dichiarazione:
const float M_PI = 3.14159265358979323846 ;

I vantaggi nelluso di const sono due: il tipo della costante dichiarato ed un eventuale errore di dichiarazione viene segnalato immediatamente dal compilatore; inoltre, la
A. De Marco

2.1 Elementi di base

costante riconosciuta, e quindi analizzabile, nelle operazioni di debug. Daltra parte,


una una costante predefinita mediante luso di un #define a volte pi comoda e immediata ( una questione sostanzialmente estetica) e pu essere usata anche per altri scopi
(per esempio per sostituire o mascherare nomi).
Il preprocessore dispone di un suo mini-linguaggio di controllo, che consiste nelle
seguenti direttive condizionali:

57

Confronto fra la direttiva


#define e lo
specificatore const

Le direttive condizionali

hespressione 1i
hblocco di direttive e/o istruzionii
#elif hespressione 2i
hblocco di direttive e/o istruzionii
#if

#else

hblocco di direttive e/o istruzionii

#endif

dove hespressionei unespressione logica che pu contenere solo identificatori di costanti predefinite o costanti letterali, ma non variabili e neppure variabili dichiarate const,
che il preprocessore non riconosce. In particolare hespressionei pu essere del tipo:
defined(hidentificatorei)

che restituisce vero se hidentificatorei definito (cio se stata eseguita la direttiva:

#define

hidentificatorei

Al posto di
#if defined(hidentificatorei)

si pu usare la forma:
#ifdef

hidentificatorei

Esiste anche la possibilit di applicare loperatore di negazione ! cosicch


!defined

hidentificatorei

restituisce vero se hidentificatorei non definito. Al posto di


#if !defined(hidentificatorei)

si pu usare la forma:

Nel costrutto di direttiva condizionale listruzione #elif sta per else if ed opzionale (possono esserci pi blocchi consecutivi, ciascuno introdotto da un #elif). Listruzione
#else opzionale (se esiste, deve introdurre lultimo blocco prima di #endif). Listruzione #endif obbligatoria e termina la sequenza iniziata con un #if. Non necessario
racchiudere i blocchi fra parentesi graffe, perch ogni blocco terminato da #elif, o da
#else, o da #endif. Il preprocessore identifica il blocco (se esiste) che corrisponde alla
prima condizione risultata vera, oppure il blocco relativo alla direttiva #else (se esiste)
nel caso che tutte le condizioni precedenti siano risultate false. Tale blocco pu contenere
sia istruzioni di programma che altre direttive, comprese direttive condizionali (possono esistere pi blocchi #if innestati): il preprocessore esegue le direttive e presenta al
compilatore le istruzioni che si trovano nel blocco selezionato, scartando sia direttive che
istruzioni contenute negli altri blocchi della sequenza #if ... #endif.
Ecco un estratto di codice reale che mostra un esempio duso delle direttive di compilazione:
Appunti di programmazione a oggetti in C++ e Matlab

ver.2009.d Copyright A. De Marco

hidentificatorei

DRAFT

#ifndef

58

Capitolo 2 Ancora sulla programmazione in C++

// Program JSBSim.cpp
// see: http://www.jsbsim.org
//
http://jsbsim.cvs.sourceforge.net/viewvc/jsbsim/JSBSim/
// ...
#if !defined(__GNUC__) && !defined(sgi) && !defined(_MSC_VER)
# include <time>
#else
# include <time.h>
#endif
#if defined(__BORLANDC__) || defined(_MSC_VER) || defined(__MINGW32__)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <mmsystem.h>
# include <regstr.h>
# include <sys/types.h>
# include <sys/timeb.h>
#else
# include <sys/time.h>
#endif
// ...
#if defined(__BORLANDC__) || defined(_MSC_VER) || defined(__MINGW32__)
double getcurrentseconds(void)
{
struct timeb tm_ptr;
ftime(&tm_ptr);
return tm_ptr.time + tm_ptr.millitm*0.001;
}
#else
double getcurrentseconds(void)
{
struct timeval tval;
struct timezone tz;

DRAFT

ver.2009.d Copyright A. De Marco

gettimeofday(&tval, &tz);
return (tval.tv_sec + tval.tv_usec*1e-6);
}
#endif

Questo frammento di codice deve stabilire quali header-file vanno inclusi al fine di accedere a delle funzioni di sistema che restituiscono lora corrente e permettano di gestire
dei calcoli in realtime (cio che avvengano rispettando una precisa scansione temporale).
Attraverso il preprocessore, nella prima direttiva condizionale viene verificata lesistenza
della costante __GNUC__ e la non esistenza delle costanti sgi ed _MSC_VER. La prima viene definita di default dai compilatori gcc e g++, che fanno parte della distribuzione GNU
[21, 24]. Le altre due sono analoghe costanti definite, rispettivamente, dal compilatore
del sistema operativo Irix, sviluppato da Silicon Graphics (SGI) [32], e dallambiente di
sviluppo Microsoft Visual C++ [27] (compilatore cl.exe). Per questioni legate alle tecnologie dei sistemi di compilazione, necessario in questo caso dover includere il file time
se __GNUC__ definita; diversamente, ad esempio, se si sta compilando con Microsoft
A. De Marco

2.2 Il mondo delle classi e degli oggetti

59

Visual Studio sar necessario leggere il file time.h.


Anche la successiva direttiva di compilazione condizionale verifica il tipo di compilatore utilizzato e include gli header-file opportuni. La costante __BORLANDC__ definita automaticamente dallambiente di sviluppo Borland C++Builder [26] (compilatore
bcc32.exe) e la costante __MINGW32__ definita dallambiente di compilazione Mingw
[25] (compilatore mingw32.exe).
Infine, pi avanti nel codice sorgente si rende necessaria la definizione della funzione getcurrentseconds che deve restituire il tempo di sistema (come numero in doppia
precisione, inclusa una parte decimale, che esprime i secondi trascorsi a partire dalla
mezzanotte dellanno 1969). Come si vede ispezionando il codice, questa funzione deve
essere definita diversamente a seconda del compilatore in uso.
Luso delle direttive di compilazione permette di architettare il codice sorgente in
maniere che possono diventare anche molto sofisticate. Uno dei vantaggi pi evidenti
che in questo modo possibile sviluppare programmi in grado di essere compilati da
diversi sistemi di sviluppo e per diversi sistemi operativi. Ci si esprime dicendo che il
software portabile e multipiattaforma.

2.2

Il mondo delle classi e degli oggetti

Il termine tipo astratto, usato in contrapposizione ai tipi nativi del linguaggio, non
molto appropriato: il C++ consente al programmatore di definire nuovi tipi, estendendo
cos le capacit effettive del linguaggio; ma, una volta definiti, questi tipi sono molto
concreti e sono trattati esattamente come i tipi nativi. Per questo motivo, la tendenza
moderna di identificare i tipi non nativi con il termine: tipi definiti dallutente (userdefined types) e di confinare laggettivo astratto a una precisa sottocategoria di questi
(di cui parleremo pi avanti).
In questa sezione parleremo dei tipi astratti comuni sia al C che al C++, usando per
la nomenclatura del C++ (oggetti, istanze, eccetera).

Tipi definiti dallutente e terminologia


Oggetti

Costruire oggetti

int ivar ;

costruisce loggetto ivar, istanza del tipo int. Dunque, istanziare un certo tipo significa
creare unistanza di quel tipo.
Appunti di programmazione a oggetti in C++ e Matlab

Creare istanzianze di un
tipo di dato

ver.2009.d Copyright A. De Marco

Il termine oggetto sostanzialmente sinonimo del termine variabile. Bench questo


termine si usi soprattutto in relazione ai tipi definiti dallutente (come strutture o classi),
si pu generalizzare il concetto, definendo oggetto una variabile di qualunque tipo, non
solo formalmente definita, ma anche gi creata e operante.
noto infatti che listruzione di definizione di una variabile non si limita a dichiarare
il suo tipo, ma crea fisicamente la variabile stessa, allocando la memoria necessaria (nella
terminologia C++ si dice che la variabile viene costruita o istanziata): pertanto la
definizione di una variabile comporta la costruzione di un oggetto.
Il termine istanza quasi simile al termine oggetto; se ne differenzia in quanto
sottolinea lappartenenza delloggetto a un dato tipo (istanza di qualcosa). Per esempio,
la dichiarazione/definizione:

DRAFT

2.2.1

Capitolo 2 Ancora sulla programmazione in C++

60

La parola chiave
typedef

Listruzione introdotta dalla parola-chiave typedef definisce un sinonimo di un tipo


esistente, cio non crea un nuovo tipo, ma un nuovo identificatore di un tipo (nativo o
astratto) precedentemente definito. Ad esempio:
typedef unsigned long int* Pul ;

definisce il nuovo identificatore di tipo Pul, che potr essere usato, nelle successive
dichiarazioni (allinterno dello stesso ambito), per costruire oggetti di tipo puntatore a
unsigned long. Ecco alcuni esempi:
unsigned long a;
Pul ogg1 = &a;
Pul parray[100]; // eccetera

Luso di typedef permette di semplificare dichiarazioni lunghe di variabili dello stesso


tipo. Per esempio, supponiamo di dover dichiarare molti array, tutti dello stesso tipo e
della stessa dimensione:
double a1[100];
double a2[100];
double a3[100]; // eccetera

Usando typedef la semplificazione evidente:


typedef double MyArray[100];
MyArray a1, a2, a3;

2.2.2
La parola chiave struct

Le strutture

Come gli array, in C++ (e in C) le strutture sono gruppi di dati. A differenza dagli array,
i singoli componenti di una struttura possono essere di tipo diverso. Ecco un esempio di
definizione di una struttura tramite la parola chiave struct (che il C++ eredita dal C):

DRAFT

ver.2009.d Copyright A. De Marco

struct Anagrafico
{
char nome[20];
int anni;
char indirizzo[30];
} ;

Dopo la parola-chiave struct segue lidentificatore della struttura, detto anche marcatore
o tag, e, fra parentesi graffe, lelenco dei componenti della struttura, detti membri o campi.
Ogni membro dichiarato come una normale variabile ( una semplice dichiarazione, non
una definizione, e pertanto non comporta la creazione delloggetto corrispondente) e pu
essere di qualunque tipo (anche array o puntatore o una stessa struttura). Dopo la parentesi
graffa di chiusura, obbligatoria la presenza del punto e virgola (diversamente dai blocchi
delle funzioni).
In C++ (e non in C) la definizione di una struttura comporta la creazione di un nuovo
tipo, il cui nome coincide con il tag della struttura. Pertanto, nellesempio appena fatto,
Anagrafico a pieno titolo un tipo (come int o double), con la sola differenza che si
tratta di un tipo astratto, non nativo del linguaggio.
Per questo motivo lenunciato di una struttura una definizione e non una semplice
dichiarazione: crea unentit (il nuovo tipo) e ne descrive il contenuto. Ma, diversamente
dalle definizioni delle variabili, non alloca memoria, cio non crea oggetti. Perch ci
A. De Marco

2.2 Il mondo delle classi e degli oggetti

61

avvenga, il nuovo tipo deve essere istanziato, esattamente come succede per i tipi nativi.
Riprendendo lesempio, listruzione di definizione:
Anagrafico davide, giovanni, cugini[30] ;

costruisce gli oggetti davide, giovanni e larray cugini, istanze del tipo Anagrafico.
Solo adesso viene allocata memoria, per ogni oggetto in quantit pari alla somma delle
memorie che competono ai singoli membri della struttura
* Loperatore sizeof( ... ) pu essere applicato sia al tipo Anagrafico sia ad una sua istanza (ad
esempio sizeof(Anagrafico) o sizeof(davide)) e restituisce il numero dei bytes allocati ad ogni istanza
di Anagrafico).

La collocazione ideale della definizione di una struttura in un header-file: conviene


infatti separarla dalle sue istanze, in quanto la definizione deve essere (di solito) accessibile dappertutto, mentre le istanze sono normalmente locali e quindi limitate dal loro
ambito di visibilit.

Gli header-file

* Potrebbe per sorgere un problema: se un programma suddiviso in pi files sorgente e tutti includono
lo stesso header-file contenente la definizione di una struttura, dopo lazione del preprocessore risulteranno
diverse translation unit con la stessa definizione e quindi sembrerebbe violata la regola della definizione unica (o one-definition-rule, ODR). Questo problema viene tipicamente risolto creando una costante
predefinita per ciascun header-file e facendo in modo che il preprocessore non lo legga pi di una volta.

Ecco come un programmatore C++ scriverebbe il file Anagrafico.h:


#ifndef ANAGRAFICO_H
#define ANAGRAFICO_H
struct Anagrafico
{

hdefinizione dei campii

} ;
#endif

inizializza a con il valore del campo


"anni" delloggetto davide
inserisce 1 nel campo anni delloggetto
giovanni

Appunti di programmazione a oggetti in C++ e Matlab

ver.2009.d Copyright A. De Marco

int a = davide.anni; //
//
giovanni.anni = 1;
//
//

Loperatore . (punto)

DRAFT

Se necessario includere questo file in diversi file sorgente, il preprocessore controller


lavvenuta definizione della macro ANAGRAFICO_H. La prima volta che il preprocessore si
trova a leggere Anagrafico.h sar quella in cui rester definita la macro ANAGRAFICO_H.
Nel resto del processo di precompilazione, se dovesse essere richiesta la rilettura del file
header, lesito del controllo #ifndef ANAGRAFICO_H sar falso e non ci sar il rischio di
ridefinire il tipo Anagrafico pi volte.
La grande utilit delle strutture consiste nel fatto che i nomi delle sue istanze possono essere usati direttamente come operandi in molte operazioni o come argomenti nelle
chiamate di funzioni, consentendo un notevole risparmio, soprattutto quando il numero
di membri elevato. In alcune operazioni, tuttavia, necessario accedere a un membro individualmente. Ci possibile grazie alloperatore binario . (punto) di accesso
al singolo membro: questo operatore ha come left-operand il nome delloggetto e come
right-operand quello del campo. Ad esempio: davide.indirizzo.
Come altri operatori che svolgono compiti analoghi (per esempio loperatore [ ] di
accesso al singolo elemento di un array), anche loperatore . pu restituire sia la lettura di
un dato (un cosiddetto r-value) che linserimento di un dato (un cosiddetto l-value). Ecco
due esempi:

62

Capitolo 2 Ancora sulla programmazione in C++

2.2.3

Dalle strutture alle classi

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut purus elit, vestibulum ut,
placerat ac, adipiscing vitae, felis. Curabitur dictum gravida mauris. Nam arcu libero,
nonummy eget, consectetuer id, vulputate a, magna. Donec vehicula augue eu neque.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Mauris ut leo. Cras viverra metus rhoncus sem. Nulla et lectus vestibulum urna
fringilla ultrices. Phasellus eu tellus sit amet tortor gravida placerat. Integer sapien est,
iaculis in, pretium quis, viverra ac, nunc. Praesent eget sem vel leo ultrices bibendum.
Aenean faucibus. Morbi dolor nulla, malesuada eu, pulvinar at, mollis ac, nulla. Curabitur auctor semper nulla. Donec varius orci eget risus. Duis nibh mi, congue eu, accumsan
eleifend, sagittis quis, diam. Duis eget orci sit amet orci dignissim rutrum.
Nam dui ligula, fringilla a, euismod sodales, sollicitudin vel, wisi. Morbi auctor lorem
non justo. Nam lacus libero, pretium at, lobortis vitae, ultricies et, tellus. Donec aliquet,
tortor sed accumsan bibendum, erat ligula aliquet magna, vitae ornare odio metus a mi.
Morbi ac orci et nisl hendrerit mollis. Suspendisse ut massa. Cras nec ante. Pellentesque
a nulla. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus
mus. Aliquam tincidunt urna. Nulla ullamcorper vestibulum turpis. Pellentesque cursus
luctus mauris.

2.3

La libreria standard

DRAFT

ver.2009.d Copyright A. De Marco

Nam dui ligula, fringilla a, euismod sodales, sollicitudin vel, wisi. Morbi auctor lorem
non justo. Nam lacus libero, pretium at, lobortis vitae, ultricies et, tellus. Donec aliquet,
tortor sed accumsan bibendum, erat ligula aliquet magna, vitae ornare odio metus a mi.
Morbi ac orci et nisl hendrerit mollis. Suspendisse ut massa. Cras nec ante. Pellentesque
a nulla. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus
mus. Aliquam tincidunt urna. Nulla ullamcorper vestibulum turpis. Pellentesque cursus
luctus mauris.
Nulla malesuada porttitor diam. Donec felis erat, congue non, volutpat at, tincidunt
tristique, libero. Vivamus viverra fermentum felis. Donec nonummy pellentesque ante.
Phasellus adipiscing semper elit. Proin fermentum massa ac quam. Sed diam turpis,
molestie vitae, placerat a, molestie nec, leo. Maecenas lacinia. Nam ipsum ligula, eleifend
at, accumsan nec, suscipit a, ipsum. Morbi blandit ligula feugiat magna. Nunc eleifend
consequat lorem. Sed lacinia nulla vitae enim. Pellentesque tincidunt purus vel magna.
Integer non enim. Praesent euismod nunc eu purus. Donec bibendum quam in tellus.
Nullam cursus pulvinar lectus. Donec et mi. Nam vulputate metus eu enim. Vestibulum
pellentesque felis eu massa.

2.4

Le librerie Boost

Le librerie Boost per il C++ sono state create con lo scopo di estendere quelle Standard.
Sono, inoltre, concepite per essere ampiamente utili ed usabili in molte applicazioni.
A. De Marco

2.5 Esempi

2.5
2.5.1

63

Esempi
Una classe completa per la lettura dati da file

Si ha il problema di leggere delle storie temporali di alcune grandezze fisiche immagazzinate in un file di testo nel formato csv (comma-separated values). Per ciascun parametro,
I dati sono contenuti in colonne. In ciascuna riga di testo i valori reali, di cui il primo un
tempo, sono separati da virgole. Per facilitare la lettura e linterpretazione delle colonne
di dati, la prima riga del file contiene dei nomi separati da virgole (stringhe di caratteri), in
numero pari al numero delle colonne di dati. Ecco un esempio di file di nome mydata.csv
con tre colonne di dati, oltre a quella dei tempi:
Time , P (deg/s), Q (deg/s), R (deg/s), P dot (deg/s^2)
0.0333332, -0.00015656, -4.292971189, 0.00002302, -0.005936929
0.0833330, -0.00062498, -7.185558850, 0.00010837, -0.010642481
0.1416661, 0.00000000, 0.000934767, -0.00000000, 0.000000434
0.1916659, 0.00000010, 0.001996633, -0.00000001, 0.000003059
0.2416657, 0.00000038, 0.000776778, -0.00000006, 0.000006922
0.2916655, 0.00000088, -0.003406548, -0.00000015, 0.000011341
0.3416653, 0.00000160, -0.010372289, -0.00000032, 0.000016014
0.3916651, 0.00000256, -0.019579002, -0.00000055, 0.000020764
0.4416649, 0.00000375, -0.030411132, -0.00000096, 0.000025527
0.4916647, 0.00000519, -0.042320402, -0.00000139, 0.000030247
0.5416645, 0.00000685, -0.054878723, -0.00000197, 0.000034896
0.5916643, 0.00000875, -0.067782347, -0.00000266, 0.000039467
0.6416641, 0.00001087, -0.080833323, -0.00000344, 0.000043918
0.6916639, 0.00001321, -0.093913868, -0.00000449, 0.000048246
...

Il file header della classe ADM::DataFile il seguente:

<fstream>
<cstdio>
<string>
<vector>
<cmath>
<iostream>

using namespace std;


/* This class handles reading a data file containing
time histories in csv format (comma-separated values):
- data are stored in columns
- first row is a comma-separated list of names
- first colum represents time (sec)
*/
Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

#include
#include
#include
#include
#include
#include

ver.2009.d Copyright A. De Marco

//------------------------------// File:
DataFile.h
// Author: Agostino De Marco
// Date:
April 2009
//------------------------------#ifndef DATAFILE_H
#define DATAFILE_H

64

Capitolo 2 Ancora sulla programmazione in C++

namespace ADM {
class DataFile {
public:
DataFile();
~DataFile();
DataFile(string fname);

DRAFT

ver.2009.d Copyright A. De Marco

std::vector <string> names;


string data_str;
typedef std::vector <double> Row;
typedef std::vector <Row> DataType;
DataType Data;
int GetNumFields(void) {
if (Data.size() > 0) return(Data[0].size());
else return(0);
}
int GetNumRecords(void) {
return(Data.size());
}
double GetStartTime(void) {
if (Data.size() >= 2) return(Data[0][0]);
else return(0);
}
double GetEndTime(void) {
if (Data.size() >= 2) return(Data[Data.size()-1][0]);
else return(0);
}
double GetMax(int column) {return(Max[column]);}
double GetMin(int column) {return(Min[column]);}
double GetRange(int field) {
return (GetMax(field) - GetMin(field));
}
void
SetStartIdx(int sidx) {StartIdx = sidx;}
void
SetEndIdx(int eidx)
{EndIdx = eidx;}
int
GetStartIdx(void)
{return StartIdx;}
int
GetEndIdx(void)
{return EndIdx;}
bool
IsOk()
{return fileGood;}
void
NicePrintNames(void);
private:
string buff_str;
ifstream f;
Row Max;
Row Min;
int StartIdx, EndIdx;
bool fileGood;
string stripSpaceLE(string str);
string stripSpaceTE(string str);
};
} // end namespace ADM

A. De Marco

2.5 Esempi

65

#endif

Ecco un esempio di possibile utilizzo della classe:


//------------------------------// File:
test1.cpp
// Author: Agostino De Marco
// Date:
April 2009
//------------------------------#include "DataFile.h"
#include
#include
#include
#include

<iostream>
<fstream>
<cstdio>
<cstdlib>

using namespace std;


using namespace ADM;
int main(int argc, char *argv[])
{
if (argc > 2 || argc == 1 ) {
cout << endl
<< "\tUsage: test1.exe <data_file_name.csv>" << endl;
exit(-1);
}
ADM::DataFile df(argv[1]);
df.NicePrintNames();

Se si lancia il programma test1.exe con la riga di comando:


>> test1.exe mydata.csv

si avr in output:
File mydata.csv successfully opened.
Done parsing names. Reading data ...
Done Reading data.
Here are the available parameters:
0) Time
1) P (deg/s)
Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

cout << endl;


cout << endl << "Data file contains "
<< numvars << " independent variables." << endl;
cout <<
"Number of data points: "
<< numpoints << endl;
cout <<
"Time goes from "
<< starttime << " to " << endtime << " seconds." << endl;
return 0;

ver.2009.d Copyright A. De Marco

int numvars
= df.GetNumFields();
int numpoints = df.GetNumRecords();
double starttime = df.GetStartTime();
double endtime
= df.GetEndTime();

66

Capitolo 2 Ancora sulla programmazione in C++

2) Q (deg/s)
4) P dot (deg/s^2)

3) R (deg/s)

Data file contains 4 independent variables.


Number of data points: 320
Time goes from 0.0333332 to 15.9916 seconds.

DRAFT

ver.2009.d Copyright A. De Marco

2.6

Come imparare il C++

Come noto, un linguaggio di programmazione ha due scopi correlati: (i) fornire i mezzi
perch il programmatore possa specificare le azioni da eseguire, e (ii) fornire un insieme
di concetti per pensare a quello che pu essere fatto. Il primo scopo richiede che il linguaggio sia vicino alla macchina, per permettere che tutti gli importanti aspetti legati alla
macchina siano trattati in una maniera ragionevolmente ovvia per il programmatore. Il
linguaggio C fu progettato con in mente questo scopo. Il secondo scopo richiede che il
linguaggio sia vicino al problema da risolvere, di modo che i concetti necessari alla soluzione siano direttamente e concisamente esprimibili. Le potenzialit aggiunte al C per
creare il C++ sono state progettate pensando a questo obiettivo.
Imparare direttamente il C++, per i programmatori che non conoscono il C, dovrebbe
essere la strada da seguire. Il C++ pi sicuro, pi espressivo e riduce il bisogno di
concentrarsi su tecniche di basso livello. Per i programmatori che conoscono il C, il
vantaggio di imparare il C++ equivale al grande vantaggio che si ha nelle lingue naturali
di essere essere almeno bilingui.
Per imparare il C++, la cosa pi importante concentrarsi sui concetti, senza perdersi
nei dettagli tecnici del linguaggio. Come sostiene Bjarne Stroustrup nel suo testo C++
Linguaggio, libreria standard, principi di programmazione [1]: si impara un nuovo
linguaggio di programmazione per diventare programmatori migliori; cio per diventare pi bravi nel progettare e realizzare nuovi sistemi e nel mantenere quelli vecchi. Per
questo motivo, arrivare a padroneggiare le tecniche di programmazione e progetto dovrebbe essere molto pi importante che comprendere subito i dettagli del linguaggio. La
comprensione, di solito, viene col tempo e la pratica.
Si deve tener presente che il C++ supporta molti stili di programmazione. Tutti sono
basati sul controllo rigoroso dei tipi (strong type checking), e hanno lo scopo di raggiungere un livello di astrazione piuttosto alto e la diretta rappresentazione delle idee
del programmatore. Ogni tecnica di programmazione pu raggiungere i suoi obiettivi
efficacemente, mantenendo efficiente luso delle risorse di tempo e memoria.
Chi pratico di altri linguaggi (ad esempio il C, il Fortran o il Matlab) dovrebbe
convincersi che per sfruttare i benefici del C++ occorre spendere tempo per imparare e
assimilare gli stili di programmazione e le tecniche adatte al C++ (specialmente il paradigma di programmazione a oggetti). Un programmatore abituato a lavorare col Fortran,
ad esempio, potrebbe certamente scrivere programmi in C++ adottando solamente il paradigma di programmazione procedurale a cui abituato. Egli deve per confrontarsi con
la realt di fatto in cui lapplicazione non meditata a un linguaggio di tecniche efficaci
in un linguaggio differente porta generalmente a un codice difficile da capire, arduo da
mantenere e di scarsa efficienza.
A. De Marco

2.6 Come imparare il C++

67

DRAFT

ver.2009.d Copyright A. De Marco

Il C++ pu essere appreso gradualmente. Il modo con cui si impara un nuovo linguaggio dipende da ci che gi si conosce e dagli scopi che ci si prefigge. Probabilmente lo
spirito migliore con cui un programmatore pu cimentarsi con il C++ non tanto quello
di acquisire una nuova sintassi con cui realizzare le stesse cose di prima, ma, piuttosto,
quello di apprendere modi nuovi e migliori di costruire sistemi. Ci equivale a cercare
di diventare un programmatore e un progettista migliore. Questo deve essere, per forza
di cose, un processo graduale, perch lacquisizione di nuove capacit richiede tempo e
pratica: basti pensare quanto tempo occorre per apprendere bene una nuova lingua o imparare a suonare un nuovo strumento musicale. Migliorare come programmatore e come
progettista di programmi senzaltro pi semplice e veloce, ma non cos semplice come
si vorrebbe.

Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

ver.2009.d Copyright A. De Marco

Capitolo 3
Strumenti di sviluppo
I quickly found out that C++ programmers are the
smartest programmers in the world, as each one I met
would quickly point out to me.
Don Box

Indice
3.1
3.2

Il Compilatore gcc in ambiente GNU/Linux o Cygwin . . . . . . . . 69


Ambienti integrati di sviluppo (IDE) . . . . . . . . . . . . . . . . . . 71

[capitolo incompleto]

3.1

Il Compilatore gcc in ambiente GNU/Linux o Cygwin

Il progetto GNU

Il comando gcc, che sta per GNU Compiler Collection, fa parte del progetto GNU [21].
Il progetto GNU fu lanciato nel 1984 da Richard Stallman con lo scopo di sviluppare un
sistema operativo di tipo Unix che fosse un software completamente liberoi free software.
ormai famoso il gioco di parole Cosa GNU? Gnu Non Unix!, che contiene una
sottile ricorsione. Riporto un estratto dal manifesto del progetto GNU http://www.gnu.
org/gnu/manifesto.html, scritto da Richard Stallman:
GNU, che sta per Gnus Not Unix (Gnu Non Unix), il nome del sistema software completo e Unix-compatibile che sto scrivendo per distribuirlo
liberamente a chiunque lo possa utilizzare. Molti altri volontari mi stanno
aiutando. Abbiamo gran necessit di contributi in tempo, denaro, programmi
e macchine.

DRAFT

3.1.1

ver.2009.d Copyright A. De Marco

Per GNU/Linux [22] e nelle finestre Cygwin [24] in ambiente Windows disponibile un
compilatore integrato C/C++ che si invoca con i comandi gcc e g++, rispettivamente. In
realta g++ uno script che chiama gcc con opzioni specifiche per riconoscere il C++.
La schermata di una finestra di Cygwin mostrata nella figura 3.1, nella quale si vede
loutput del comando gcc -v che ottiene la versione del compilatore gcc in uso.

70

Capitolo 3 Strumenti di sviluppo

3.1.2

I passi della compilazione

Sia gcc che g++ processano file di input attraverso uno o pi dei seguenti passi:
1. preprocessing, cio
la rimozione dei commenti, e
linterpretazione di speciali direttive per il preprocessore denotate da #, come:
#include, che include il contenuto di un determinato file, #define, che definisce
un nome simbolico o una variabile, eccetera;
2. compilation, cio la traduzione del codice sorgente ricevuto dal preprocessore in
codice assembly;
3. assembly, cio la creazione del codice oggetto;
4. linking, ovvero la combinazione delle funzioni definite in altri file sorgenti o definite
in librerie con la funzione main() per creare il file eseguibile.

Figura 3.1 Schermata di una finestra dei comandi Cygwin [24]. mostrato loutput del comando
gcc -v

DRAFT

ver.2009.d Copyright A. De Marco

3.1.3

che ottiene la versione del compilatore gcc in uso.

Lutilit make

Il programma make una utility usata con i sistemi operativi della famiglia UNIX che
automatizza il processo di conversione dei file da una forma ad unaltra, risolvendo le
dipendenze e invocando altri programmi per il lavoro necessario alle diverse operazioni.
Molto frequentemente make usato per la compilazione di codice sorgente in codice
oggetto, unendo e poi collegando il codice oggetto in una forma finale corrispondente ad
un esegubile o ad una libreria. Il comando make usa file chiamati makefiles per determinare il grafico delle dipendenze per un particolare output, e gli script necessari per la
compilazione da passare alla shell dei comandi (ad esempio una shell Bash). Il termine
makefile proviene dal nome di default makefile o Makefile che make va a cercare se
invocato senza parametri aggiuntivi.
Ecco un semplice esempio di Makefile che serve a creare leseguibile myfgfsclient.exe
a partire dalla compilazione dei file myfgfsclient.cpp e datafile.cpp
# Makefile for the program myfgfsclient
VERSION = 0.1
A. De Marco

3.2 Ambienti integrati di sviluppo (IDE)

71

CC
= g++
OPTIMIZE = -g -Wall
CFLAGS
= $(DEFINES) $(OPTIMIZE)
LFLAGS
= -lstdc++ -lm
PROGS
= myfgfsclient.exe
PROGS_O = myfgfsclient.o datafile.o
LIBS
=
all:
objs progs
progs:
$(CC) $(CFLAGS) $(LFLAGS) -o $(PROGS) $(PROGS_O) $(LIBS)
objs: $(PROGS_O)
.cpp.o:
$(CC) $(CFLAGS) -c -o $*.o $<
.o:
$(CC) $(CFLAGS) $(LFLAGS) -o $* $(PROGS_O) $(LIBS)
clean: cleanbin
rm -f *.o *~
cleanbin:
rm -f $(PROGS)
dep:
rm -f .depend
make .depend
tar:
tar czvf myfgfsclient.tgz Makefile \
myfgfsclient.cpp datadile.h datafile.cpp README TODO

Posizionandosi nella cartella in cui risiede il


lutente potr lanciare il comando:

Makefile

ed i sorgenti del programma

3.2

Ambienti integrati di sviluppo (IDE)

Un Integrated Development Environment (IDE) unapplicazione interattiva che aiuta lo


sviluppatore di software (cio il programmatore) a costruire un progetto. Un progetto un
insieme di file, contenenti codice sorgente, che vengono letti ed elaborati dal compilatore
separatamente e poi collegati insieme (tramite il linker) per costruire un unico file in
codice binario, contenente il programma eseguibile, che pu essere a sua volta lanciato
dallo stesso IDE o autonomamente da sistema operativo.
Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

ottenendo la creazione del file mtfgfsclient.exe se questo era assente o possiede una
data di creazione precedente a uno qualsiasi dei file da cui dipende (sorgenti o oggetto).
Per approfondimenti sulle funzionalit avanzate della utility make si rimanda il lettore
alla consultazione del manuale online di GNU make [23].

ver.2009.d Copyright A. De Marco

>> make

72

Capitolo 3 Strumenti di sviluppo

Figura 3.2 Schermata di una sessione di lavoro con Microsoft Visual C++ 2008 Express [27].

Lo sviluppatore interagisce con lambiente di svilutto tramite menu di tipo pop-up (a


tendina); in genere le voci di menu pi significative sono selezionabili anche tramite toolbars (gruppi di icone) o tramite i cosiddetti acceleratori (tasti della keyboard che eseguono
la stessa funzione della corrispondente voce di menu).
Un IDE pu aprire sullo schermo e usare parecchie finestre contemporaneamente,
contenenti i files sorgente (uno per ogni finestra), loutput del programma, le informazioni acquisite in fase di debug, eccetera. Possono esistere anche finestre che contengono
lelenco dei file, delle funzioni, o anche delle singole variabili utilizzate; cliccando su
queste voci si pu raggiungere rapidamente la parte di programma che interessa esaminare
o modificare.

DRAFT

ver.2009.d Copyright A. De Marco

3.2.1

Lambiente di sviluppo Microsoft Visual Studio

Qui illustreremo brevemente lutilizzo di Microsoft Visual C++, disponibile per il sistema operativo Windows (http://www.microsoft.com/express/vc/). Il Visual C++ non
soltanto un IDE, ma un linguaggio vero e proprio, essendo dotato di funzionalit e librerie
che vanno ben oltre lo standard C++. Ci limiteremo, per, ad illustrare il suo ambiente di
sviluppo, nella versione ridotta per applicazioni che utilizzano solo codice standard.

3.2.2

Lambiente di sviluppo Code::Blocks

Lambiente di sviluppo Code::Blocks un IDE libero, open source e multipiattaforma


(http://www.codeblocks.org/). scritto in C++ usando le librerie del toolkit grafico
wxWidgets [31]. Lambiente utilizza unarchitettura basata su plugin, le sue capacit
e caratteristiche sono estese proprio dai plugin installati dallutente a seconda delle sue
esigenze. Attualmente, Code::Blocks orientato verso il C/C++.
Code::Blocks disponibile per Windows, GNU/Linux e Mac OS X.
A. De Marco

DRAFT

Figura 3.3 La finestra di esplorazione della solution in Microsoft Visual C++ 2008 Express.

73

ver.2009.d Copyright A. De Marco

3.2 Ambienti integrati di sviluppo (IDE)

Appunti di programmazione a oggetti in C++ e Matlab

74

Capitolo 3 Strumenti di sviluppo

Figura 3.4 La finestra di configurazione delle propriet di un progetto in Microsoft Visual C++

DRAFT

ver.2009.d Copyright A. De Marco

2008 Express (Menu Project, Properties; oppure clic con pulsante destro sulla voce del progetto
nella finestra di esplorazione, Properties dal menu a tendina, come nella figura 3.4). I parametri
del del processo di compilazione e collegamento possono essere impostati per tutte le possibili
configurazioni (Debug, Release) o resi specifici per ciascuna di esse.

Figura 3.5 Particolare della configurazione delle propriet di compilazione (C/C++, General). In

questo esempio lutente ha impostato un percorso aggiuntivo, oltre a quello di default del compilatore Microsoft, per la ricerca degli header file (Additional Include Direcoties).

A. De Marco

3.2 Ambienti integrati di sviluppo (IDE)

75

Figura 3.6 Particolare della configurazione delle propriet di collegamento (Linker, General). In

File (figura 3.6). Visual Studio mostra i risultati dellespansione di un nutrito numero di simboli
predefiniti, tra cui i percorsi di sistema $(FrameworkDir) e $(FrameworkSDKDir).

Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

Figura 3.7 Finestra di dialogo attraverso la quale possibile modificare il percorso dellOutput

ver.2009.d Copyright A. De Marco

questo esempio lutente ha impostato un percorso personalizzato per il file eseguibile. Nella casella
Output File si ha la macro $(ProjectDir)..\test\$(ProjectName).exe, che Visual Studio espander
sostituendo dei percorsi concreti ai simboli $(ProjectDir) e $(ProjectName).

76

Capitolo 3 Strumenti di sviluppo

Figura 3.8 La finestra di output (o di log) della compilazione e del collegamento (Menu Project,

Figura 3.9 Schermata di una sessione di lavoro con Code::Blocks [28].

DRAFT

ver.2009.d Copyright A. De Marco

Build hnome progettoi).

A. De Marco

Parte II

DRAFT

ver.2009.d Copyright A. De Marco

Programmazione a oggetti
con Matlab

Acquisisci nuove conoscenze mentre rifletti sulle vecchie,


e forse potrai insegnare ad altri.

DRAFT

ver.2009.d Copyright A. De Marco

Confucio

Capitolo 4
Una panoramica su Matlab
Jesce sole, jesce sole, nun ce fa cchi suspir!
Gatta Cenerentola

Indice
4.1
4.2
4.3
4.4
4.5
4.6

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

.
.
.
.
.
.

79
80
81
84
84
91

Matlab un linguaggio di programmazione di alto livello nato per il calcolo tecnicoscientifico. Le funzionalit di Matlab in quanto applicazione rispecchiano la filosofia con
cui essa stata progettata e sviluppata via via negli anni. Lambiente di lavoro strutturato in modo da risultare particolarmente utile nei seguenti campi applicativi: (i) Analisi
numerica e calcolo, (ii) sviluppo di codice e scripting, (iii) modellistica, simulazione
e prototyping, (iv) analisi di dati, esplorazione e visualizzazione, (v) disegno industriale e scientifico, (vi) sviluppo di applicazioni stand-alone corredate di interfaccia utente
(Graphical User Interface, GUI).
Matlab, il cui nome deriva da Matrix Laboratory, inteso come ambiente di sviluppo,
fornisce un linguaggio per il calcolo che ha come struttura di base la matrice. Gli scalari
sono matrici 1  1, i vettori riga sono matrici 1  n ed i vettori colonna sono matrici
m  1. Le due dimensioni principali di questi array vengono dedotte dal contesto delle
istruzioni di assegnamento. In momenti successivi sempre possibile un reshaping, cio
un adattamento di una data matrice m  n a delle nuove dimensioni m0  n0 attraverso il
riposizionamento, laggiunta o la sottrazione di elementi.
Esiste anche la possibilit, come accade per altri linguaggi di programmazione, di
definire ed utilizzare array multidimensionali. Questi sono delle generalizzazioni delle
matrici e costituiscono delle collezioni di dati accessibili mediante tre o pi indici.

ver.2009.d Copyright A. De Marco

Elementi di base

DRAFT

4.1

Elementi di base . . . . . . . . .
Toolboxes e Simulink . . . . . .
Sessioni di lavoro . . . . . . . . .
Usare lhelp . . . . . . . . . . . .
Nozioni e notazioni fondamentali
Operazioni di Input / Output . .

80

Capitolo 4 Una panoramica su Matlab

Matlab esegue tutti i calcoli numerici con numeri complessi in doppia precisione e la
grande maggioranza delle operazioni e delle funzioni predefinite sono implementate in
modo da lavorare nativamente su entit matriciali. Ci particolarmente vantaggioso nella risoluzione di molti problemi di calcolo dellingegneria, in particolare quelli formulati
teoricamente nei testi con agevoli notazioni vettorali e matriciali. Tali problemi possono essere implementati in linguaggio Matlab e risolti efficientemente attraverso comandi
semplici, con un minimo utilizzo di cicli. Ad esempio, per la soluzione dei tipici problemi dellalgebra lineare possibile, con poche righe di codice e senza un appesantimento
del carico di lavoro del programmatore, richiamare un numero di algoritmi ben collaudati
basati sulle funzionalit di librerie come LINPACK, LAPACK o EISPACK.
I codici di calcolo in linguaggio Matlab sono generalmente pi snelli rispetto a quelli
che sarebbe necessario sviluppare con un linguaggio scalare come il C o il Fortran 77.
Anche se linguaggi di programmazione pi evoluti come il C++, con il paradigma di programmazione a oggetti, e come il Fortran 90/95, con luso avanzato dei moduli, permettono ormai una implementazione snella ed efficiente di una vasta categoria di algoritmi, la
popolarit di Matlab, con il suo linguaggio intuitivamente pi semplice da imparare e con
la sua ben riuscita integrazione tra lo strumento di sviluppo codice, il debugger e loutput
grafico, rende il laboratorio della matrice uno strumento pi attraente per chi si accosta
al mondo della programmazione o per chi ha in mente tempi di sviluppo ridotti.
Lambiente Matlab si evoluto durante gli anni grazie al feedback ed al contributo dei
molti utenti sparsi per il mondo. In ambienti universitari ormai uno strumento didattico
standard per corsi introduttivi e corsi avanzati, nella matematica, nellingegneria e nelle
scienze.

DRAFT

ver.2009.d Copyright A. De Marco

4.2

Toolboxes e Simulink

Lambiente possiede dei gruppi tematici di funzioni, denominate toolboxes (cassette degli
attrezzi), pensati ed implementati ad hoc per classi particolari di problemi. Essi risultano
molto utili per la maggior parte degli utenti di Matlab e sono pensati per fornire allutente degli strumenti di calcolo preconfezionati, efficienti e specializzati. I toolboxes sono
collezioni complete di funzioni Matlab che estendono lambiente di lavoro e permettono
di risolvere particolari categorie di problemi. Le aree scientifiche per le quali sono disponibili dei toolboxes specifici sono numerose. Alcuni esempi sono: lelaborazione dei
segnali, i sistemi di controllo, le reti neurali, le wavelets, la simulazione in generale, il
calcolo simbolico, la realt virtuale.
Di particolare importanza il pacchetto software Simulink, utilizzabile come modulo
dellambiente Matlab, e concepito per la modellazione e lanalisi di sistemi dinamici.
Simulink supporta sia modelli lineari che non lineari, a tempo continuo e discreto. Si
possono modellare sistemi a vettore di stato di dimensione qualsiasi e di tipo multirate, in
cui diversi sottogruppi di variabili di stato sono aggiornate con frequenze differenti.
Per la modellazione Simulink fornisce uninterfaccia a finestre, che si avvia digitando
il comando simulink nella finestra dei comandi di Matlab, figura 4.4, o con lapposito
tasto di avvio nellinterfaccia principale. Luso della GUI di Simulink permette la costruzione di un modello a partire dal disegno, dalla collezione ed dalla interconnessione
di blocchi funzionali, proprio come si farebbe in una fase di analisi preliminare di un sistema (prototyping) con luso di carta e penna. Simulink dotato di unampia libreria
A. De Marco

4.3 Sessioni di lavoro

81

di blocchi e di tipi di connessioni preconfezionati. Lutente pu ovviamente personalizzare delle entit gi presenti nelle librerie di default o crearne delle proprie. Si rimanda
allhelp di Matlab per approfondimenti sulle modalit di implementazione delle cosiddette
S-functions.
Un pacchetto di funzioni Simulink di notevole utilit nel campo aerospaziale ed in
particolare nella dinamica del volo costituito dal toolbox Aerospace Blockset (nelle versioni di Matlab non inferiori alla 6.5) per lanalisi e lintegrazione di sistemi dinamici che
modellano il funzionamento di velivoli di vario genere e di sistemi propulsivi.

4.3

Sessioni di lavoro

con le variabili definite al momento dellultimo comando; in basso a sinistra la storia dei comandi
pi recenti; a destra la finestra con il prompt dei comandi.

Una volta avviata lapplicazione, il layout di default della finestra principale di Matlab,
come si vede dalla figura 4.1, si presenta come linsieme di pi sotto-finestre: la finestra
con il prompt dei comandi, la finestra del workspace con le variabili definite al momento
dellultimo comando ed una finestra con la storia dei comandi pi recenti. La command
Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

Figura 4.1 Il layout di default della finestra principale di Matlab: in alto a sinistra il workspace

ver.2009.d Copyright A. De Marco

Di solito Matlab viene lanciato selezionando lapplicazione da un ambiente a finestre come Windows o fornendo il comando matlab in una delle shell di comandi Unix/Linux.
Matlab a sua volta un programma con una riga dei commandi ed un suo prompt >>.
Appena lanciato compare per qualche istante, prima dellavvio vero e proprio dellapplicazione e durante il caricamento di tutti i moduli software necessari al suo funzionamento,
una finestra (splash screen) con il logo di Matlab, la versione ed il tipo di licenza. Se ci
accade significa che tutto stato configurato correttamente, in caso contrario necessario
controllare la validit dellinstallazione o della licenza.

82

Capitolo 4 Una panoramica su Matlab

Figura 4.2 Leditor di M-files di Matlab in grado di aprire pi di un file di script alla volta e
corredato di tasti per lavvio e la gestione del debug.

window svolge anche il ruolo di finestra di log dei messaggi allutente. In questo senso si
presenta come estensione di una shell come la finestra DOS in Windows o della console
in Unix/Linux. Per accedere ad uno dei comandi della shell del sistema operativo o anche lanciare un eseguibile esterno basta digitare il comando stesso preceduto dal carattere
!, detto shell escape command. Ad esempio per conoscere velocemente il contenuto
della directory di lavoro corrente basta dare il comando !dir in Windows o !ls -l in
Unix/Linux (o anche in Windows se installato Cygwin).

DRAFT

ver.2009.d Copyright A. De Marco

Un numero di comandi di Matlab pu essere collezionato in un file, di estensione


.m, ed interpretato dal kernel, che il nucleo e motore numerico del programma. In
gergo informatico tali files, detti M-files, sono degli script di comandi perch eseguiti in
maniera sequenziale.
Un comando corrispondente al nome (estensione esclusa) di un M-file noto allambiente di lavoro se il file si trova nel percorso di ricerca predefinito. La ricerca da parte
dellinterprete dei comandi avviene in alcune sottocartelle della cartella in cui installato il programma e nella cartella di lavoro (current directory), che lutente pu definire
arbitrariamente a seconda delle sue esigenze.
Oltre alla finestra principale con il prompt dei comandi, fanno parte dellambiente
di lavoro: (i) leditor degli script di comandi (M-file editor), si veda la figura 4.2, che si
avvia dal menu File!New!M-file, e (ii) le finestre dei grafici che lutente produce nella
sua sessione di lavoro, ad esempio con il comando plot.
Se si digita plot(0:0.1:1) appare la finestra riportata nella figura 4.3. Essa contiene
un grafico in cui le ordinate corrispondono ai numeri ottenuti a partire da 0 con incrementi
di 0:1 fino ad arrivare a 1 e le ascisse corrispondono agli indici interi che vanno da 1 fino
ad 11. La sequenza di coppie di coordinate viene rappresentata per default unendo i punti
con una linea continua. Come vedremo tra breve, loperatore : consente di ottenere
rapidamente un vettore di numeri.
A. De Marco

4.3 Sessioni di lavoro

83

DRAFT

Figura 4.4 Linterfaccia grafica di Simulink per la modellazione di sistemi dinamici.

ver.2009.d Copyright A. De Marco

Figura 4.3 Una finestra grafica di Matlab ottenuta con il semplice comando plot(0:0.1:1).

Appunti di programmazione a oggetti in C++ e Matlab

84

Capitolo 4 Una panoramica su Matlab

4.4

Usare lhelp

DRAFT

ver.2009.d Copyright A. De Marco

Se si vogliono avere informazioni su Matlab si pu ovviamente ricorrere ai manuali oppure utilizzare Matlab stesso attraverso il suo sistema di help. Ad esempio, se si vuole
sapere a cosa serve il comando plot si pu scrivere help plot nella stessa finestra dei
comandi. Comparir una descrizione accurata di tale comando, sullo stile del comando
man della shell di Linux.
In generale, il comando help <nome-comando> restituisce (se il nome del comando
esiste) lhelp del comando in questione. Unaltro comando che talvolta si dimostra utile
lookfor <parola-chiave> che controlla se disponibile lhelp per la parola chiave
indicata tra tutti i comandi disponibili in Matlab.
In alternativa alla richiesta di help da riga di comando, utile per una consultazione
veloce ma a volte poco ricca di esempi, possibile cercare aiuto nella preziosa finestra di
help dellapplicazione, figura 4.5.

Figura 4.5 La finestra di help di Matlab.

4.5
4.5.1

Nozioni e notazioni fondamentali


Le variabili

Come in tutti gli ambienti di programmazione interattivi in Matlab possibile definire


delle variabili, alle quali assegnare determinati valori, ed utilizzarle in calcoli successivi.
I nomi delle variabili Matlab possono essere qualsiasi, ma non devono cominciare con un
carattere che sia un numero o uno dei caratteri speciali (come quelli di punteggiatura).
Se una variabile ha lo stesso nome di un comando Matlab, questultimo non sar pi
A. De Marco

4.5 Nozioni e notazioni fondamentali

85

disponibile a meno che la variabile non venga cancellata con il comando clear <nomevariabile>. Per assegnare una variabile si usa loperatore di assegnamento =. Il tipo
della variabile viene automaticamente costruito in base alla quantit che si assegna.
Negli esempi seguenti si riportano delle sequenze di comandi digitati al prompt di
Matlab, ma che potrebbero anche essere contenuti in un M-file. Le righe in cui il prompt
>> non compare costituiscono ci che invece la command window restituisce allutente,
detto in gergo message log. Ad esempio lassegnazione di una data variabile non terminata
dal carattere ; viene sempre seguita dal messaggio che mostra allutente il valore della
variabile e le dimensioni della matrice. Ci si verifica anche solo digitando il nome di
una variabile ed utile per conoscerne velocemente il valore o sapere se ne esiste una o
se esiste una funzione con quel nome.
Maggiori dettagli sulla gestione delle variabili si evincono dagli esempi riportati in
quanto segue. Si osservi che il carattere % quello che inizia un commento al codice
Matlab.
Per cominciare si prenda in esame la sequenza di comandi:
>> a=1
a =
1

% assegna alla variabile a il valore 1

>> b=pippo
b =
pippo

% assegna alla variabile b la stringa pippo

>> c=23;

% con il carattere ; a fine comando non viene mostrato


% il valore di c

>> 12.5
ans =
12.5000

% 12.5, viene assegnato alla variabile di default ans(wer)

>> whos
Name

% interroga il sistema sulle variabili dichiarate


Size
Bytes
Class

a
ans
b
c

1x1
1x1
1x5
1x1

8
8
10
8

double array
double array
char array
double array

>> a=pluto;
% se si riassegna una variabile questa viene riallocata
>> whos
Name
Size
Bytes
Class
a
ans
b
c

1x5
1x1
1x5
1x1

>> clear a

10
8
10
8

char array
double array
char array
double array

% si cancella a

>> who
% le variabili dichiarate (senza tipo) ora sono
Your variables are:
ans
b
c
Grand total is 12 elements using 36 bytes
>> clear all
>> who

% si cancellano tutte le variabili


% nessuna risposta, nessuna variabile definita

Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

Il significato dei comandi e delle operazioni riportate spiegato dai commenti di cui
sono corredati. Gli esempi di comandi che seguono illustrano ulteriori aspetti dellambiente di programmazione interattivo.

ver.2009.d Copyright A. De Marco

Grand total is 8 elements using 34 bytes

86

Capitolo 4 Una panoramica su Matlab

4.5.2

Matrici e vettori

Una matrice viene delimitata da parantesi quadre. Ogni riga termina con un punto e
virgola e deve avere lo stesso numero di elementi (separati da spazi bianchi o da una
virgola). Ad esempio, i comandi seguenti:
>> A=[-1 2 3; 4 5 6]
A =
-1
2
3
4
5
6
>> A=[-1, 2, 3;
4, 5, 6] % il comando termina alla seconda riga con la ]
A =
-1
2
3
4
5
6
>> A=[-1 2 3; 4 ... % i 3 punti indicano continuazione su riga successiva
5 6];

rappresentano modi equivalenti di introdurre la stessa matrice 2  3. Un vettore riga di


dimensione n una matrice 1  n, un vettore colonna di dimensione m una matrice
m  1.
Matrici con dimensioni compatibili possono essere sommate o moltiplicate con gli
operatori +, - e *. Si osservi che il carattere (apice) che segue il nome di una
variabile equivale alloperatore di trasposizione applicato alla matrice che tale variabile
rappresenta. A tal riguardo si prendano in esame i comandi che seguono:
>> A=[1 2 3; 4 5 6]; B=[7 8 9; 10 11 12];
>> A+B
% somma di due matrici 2x3
ans =
8
10
12
14
16
18
>> A=[1 2 3; 4 5 6]; B=[7 8 9; 10 11 12];
>> A*B % prodotto di una matrice 2x3 con una 3x2
ans =
50
68
122
167

DRAFT

ver.2009.d Copyright A. De Marco

Se le matrici che compaiono in unoperazione di prodotto non hanno dimensioni


compatibili, viene segnalato un messaggio di errore. Ad esempio:
>> A=[1 2 3; 4 5 6]; B=[7 8 9; 10 11 12];
>> A*B
% prodotto di una matrice 2x3 con una 2x3
??? Error using ==> *
Inner matrix dimensions must agree.

Se invece si prova ad usare loperatore .* al posto del solo * si ottiene:


>> A.*B
ans =
7
40

16
55

27
72

In questa circostanza Matlab non segnala alcun errore perch ora il prodotto stato stato
eseguito elemento per elemento (element-wise). Facendo precedere il carattere . alle
operazioni elementari queste vengono eseguite elemento per elemento. Questa una
propriet importante ed utile, ad esempio, nella produzione di grafici di funzioni.
Quando si opera con matrici quadrate possono effettuarsi su di esse diverse operazioni
particolari che restituiscono le seguenti quantit:

A. De Marco

4.5 Nozioni e notazioni fondamentali

87

>> A=[1 2; 3 4];


>> inv(A)
% linversa di A
ans =
-2.0000
1.0000
1.5000
-0.5000
>> det(A)
% il determinante
ans =
-2
>> eig(A) % gli autovalori
ans =
-0.3723
5.3723
>> cond(A) % il numero di condizionamento in norma 2
ans =
14.9330
>> norm(A) % la norma 2
ans =
5.4650
>> norm(A,inf) % la norma infinito
ans =
7

A questo punto si sperimenti una prima operazione su unintera matrice. Il comando


A+1 applicato alla matrice precedentemente definita fornisce:
>> A+1
ans =
2
4

3
5

cio tutti gli elementi di A sono stati aumentati di 1. Quindi in questo senso si pu
sommare un numero ed una matrice.
Per accedere allelemento (i,j) di una matrice A, basta scrivere A(i,j), mentre per
accedere allelemento i-mo di un vettore v (riga o colonna che sia) basta scrivere v(i).
Per accedere ad unintera colonna o ad unintera riga gli indici corrispondenti possono
essere rimpiazzati dai due punti : come negli esempi seguenti:
>> A(2,1)
ans =
4

4.5.3

Altri tipi di dati

Una variabile pu contenere anche valori non numerici. Un esempio comune dato dai
caratteri e dalle stringhe di caratteri. Le costanti stringhe di caratteri come, ad esempio
pippo, vengono delimitate con degli apici, e con lassegnazione seguente:
>> str = pippo
str =
pippo

si definir una variabile str da intendersi come un vettore i cui elementi sono i singoli
caratteri della stringa. Ad esempio il comando str(3) restituir il carattere p.
Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

>> A(1,:) % estrae la prima riga


ans =
2
3

ver.2009.d Copyright A. De Marco

>> A(:,2) % estrae la seconda colonna


ans =
3
5

88

Capitolo 4 Una panoramica su Matlab

Le recenti versioni di Matlab si sono arricchite di nuovi tipi di dati. Un esempio


costituito dai cell arrays, cio delle variabili costruite con una notazione basata sulle
parentesi graffe. Un primo esempio dato dallassegnazione: M = {a b c}. Unassegnazione equivalente si ha mediante luso del costruttore cell. Lesempio precedente pu
riscriversi come:
>> M = cell(1,3);
M{1} = a; M{2} = b; M{3} = c;

% crea un cell array di valori nulli


% riempie le celle

Le matrici a, b, e c possono essere oggetti qualunque. Ad esempio listruzione M = {A,


det(A), [det(A);det(B)]} perfettamente valida.
possibile inoltre definire delle strutture simili alle struct del linguaggio C, ai cui
campi si accede con loperatore . (punto). Un esempio di definizione di una variabile
struttura S il seguente:
>> S.name = Ed Plum;
>> S.score = 83;
>> S
S =
name: Ed Plum
score: 83

e naturalmente possibile maneggiare interi array di strutture sfruttando i vantaggi offerti


dal linguaggio.

DRAFT

ver.2009.d Copyright A. De Marco

4.5.4

Le funzioni in Matlab

Matlab, sia un linguaggio di programmazione che un ambiente computazionale interattivo. Come gi detto, i files che contengono comandi, cio righe di codice Matlab, sono
chiamati M-files. Dopo aver creato un M-file usando un qualsiasi editor di testo, salvandolo in un appropriato percorso lutente pu disporre di un nuovo comando personalizzato
o di una nuova funzionalit, che arricchisce quelle predefinite dellambiente Matlab. In
particolare esistono due tipi di M-file:
 scripts, cio comandi che non hanno argomenti di input o argomenti di output ed
operano su dati gi presenti nel workspace e/o dati creati contestualmente,
 functions, che possono accettare anche un numero variabile di argomenti di input
(come le funzioni C++) ed hanno uno o pi argomenti di output.
I programmatori in linguaggio Matlab creano in genere i propri M-files nella cartella
di lavoro o in una delle sue sottocartelle. Eventualmente gli M-file potranno essere organizzati in toolboxes e salvati in cartelle che lutente pu aggiungere al percorso di ricerca
di Matlab. Se si duplicano nomi di funzioni, cio si hanno nomi di files identici in pi
punti del percorso di ricerca, Matlab esegue quello che trova per primo. Per editare i contenuti di un M-file nella cartella di lavoro possibile selezionare ed esplorare la finestra
Current directory, cio una delle tabbed window mostrata nella figura 4.6, ed aprire
con un doppio click lM-file desiderato. Si avvier anche in questo caso lM-file editor di
Matlab.
Quando si richiama uno script dalla riga di comando, Matlab esegue semplicemente
i comandi presenti nel file. Gli script possono operare su dati esistenti nel workspace,
o possono essi stessi contenere comandi che allocano ed assegnano dati su cui operare.
Sebbene gli script non forniscono dati di output, qualsiasi variabile da essi creata rimane
A. De Marco

4.5 Nozioni e notazioni fondamentali

89

Figura 4.6 La finestra Current directory

per lesplorazione della cartella di lavoro.

nel workspace, per essere usata in calcoli successivi. Inoltre, gli script possono produrre
dei grafici, usando funzioni come plot.
Per esempio, se si crea un file chiamato myrmatrix.m contenente i seguenti comandi:
% Calcoli varii su matrici
r = zeros(1,32);
for n = 3:32
% determinanti di
r(n) = det(rand(7)); % matrici 7x7 a
end
% elementi random
r
bar(r)

con il comando >> myrmatrix si chiede a Matlab di eseguire le istruzioni contenute nel file
una dopo laltra: verr calcolato il determinante di 30 matrici 7  7 con elementi random,
comando det(random(7)), e tracciato il grafico del risultato, bar(r). Dopo lesecuzione
del comando, la variabile r rimane nel workspace.
I comandi eig, det, inv e cos via, sono esempi di funzioni Matlab. Esse possono
essere richiamate in molti modi diversi. La sintassi pi generale la seguente:

Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

dove <out1 >, ... , <outn > sono i parametri di output, mentre <in1 >, ... , <inm > sono i parametri di input. Il loro numero pu variare e per lanciare correttamente una funzione necessario consultare attentamente lhelp. Se la funzione ha un solo parametro di
output, le parentesi quadre possono essere omesse.
Le funzioni possono accettare argomenti in input e forniscono argomenti in uscita.
Il nome dellM-file e della funzione deve essere lo stesso e deve trovarsi nel percorso di
ricerca corrente. Le variabili definite allinterno di una funzione, dette variabili locali,
hanno visibilit (scope) locale alla funzione quando non sono dichiarate con attributo
global. come se le variabili locali di una funzione fossero residenti in un workspace
proprio, separato dal workspace a cui si accede dal prompt dei comandi di Matlab, che
viene cancellato dopo la chiamata.
Un semplice esempio fornito dalla funzione rank. LM-file rank.m disponibile
nella cartella <matlab root>/toolbox/matlab/matfun. La funzione pu essere chiamata

ver.2009.d Copyright A. De Marco

>>[<out1 >, ... , <outn >] = <nome-funzione>(<in1 >, ... , <inm >);

90

Capitolo 4 Una panoramica su Matlab

dopo aver definito una matrice A, digitando al prompt dei comandi >>
in questione qui di seguito riportato:

rank(A).

LM-file

% File: rank.m
function r = rank(A,tol)
% RANK Matrix rank.
% RANK(A) provides an estimate of the number of linearly
% independent rows or columns of a matrix A.
% RANK(A,tol) is the number of singular values of A
% that are larger than tol.
% RANK(A) uses the default tol = max(size(A)) * norm(A) * eps.
s = svd(A);
if nargin==1
tol = max(size(A)) * max(s) * eps;
end
r = sum(s > tol);

DRAFT

ver.2009.d Copyright A. De Marco

La prima linea non commentata di un M-file contenente una funzione comincia con la
parola chiave function seguita dalla lista degli argomenti di output, in questo caso la sola
r, dal nome alla funzione, rank, e dalla lista di argomenti di input racchiusi tra parentesi
tonde, (A,tol). Le righe seguenti la definizione iniziano col carattere di commento %
e rappresentano dei commenti di aiuto che verranno visualizzati al prompt dei comandi
quando si digita >>help rank. La prima linea del testo di aiuto la cosiddetta H1 line
che Matlab espone quando si ricerca aiuto da riga di comando. Il resto del file rappresenta
il codice interpretato da Matlab per rendere disponibile la funzione agli utenti.
Nellesempio appena riportato la variabile s presente nel corpo della funzione, cos
come le variabili che compaiono nella definizione, r, A e tol, sono locali alla funzione e rimangono indipendenti e separate da qualsiasi variabile nel workspace di Matlab.
Anche se in questultimo fosse definita una variabile con lo stesso nome non vi sarebbe
possibilit di conflitto.
Lesempio illustra un aspetto delle funzioni di Matlab che si trova anche in altri linguaggi di programmazione come il C++ o il Fortran 90/95 cio la possibilit di definire
funzioni che possano essere chiamate con un numero variabile di argomenti. Il numero
di argomenti effettivamente passato alla funzione nel momento della chiamata viene memorizzato di volta in volta nella variabile riservata nargin. Una funzione come rank pu
dunque essere usata in modi diversi come si vede dallesempio seguente:
>>
>>
>>
>>

A = rand(10); % genera una matrice 10x10 ad elementi random


rank(A);
r = rank(A);
r = rank(A,1.e-6);

Rispetto alla gestione del numero di parametri di input e di output, molte funzioni che
fanno parte del linguaggio Matlab sono definite in maniera simile. Se nessun argomento
di output compare nella chiamata, il risultato assegnato alla variabile di default ans.
Tipicamente il primo argomento di input sempre richiesto e non pu essere omesso
nella chiamata. Se il secondo argomento di input non presente, la funzione lo pone
uguale ad un valore di default. Tipicamente nel corpo di una funzione sono interrogate
le due variabili riservate nargin e nargout, che contengono il numero di argomenti di
input e di output effettivamente utilizzati nelle chiamate. Leffettivo comportamento della
funzione ed eventualmente lincorrere in una condizione di errore di chiamata dipender
di volta in volta dal valore di queste due variabili.
A. De Marco

4.6 Operazioni di Input / Output

4.6

91

Operazioni di Input / Output

Lambiente di lavoro offre la possibilit di analizzare dati raccolti eventualmente con altri
programmi ed immagazzinati in files di testo (formato ASCII). Ad esempio, si potrebbe
avere a disposizione linsieme di dati seguente:
1
2
3
4
5
6
7
8
9
10

2.000
0.2500
0.0740
0.0310
0.0160
0.0090
0.0050
0.0030
0.0020
0.0020

-5.0
-9.1
-23.0
-53.2
-105.1
-185.5
-299.4
-453.7
-653.0
-905.3

Questi dati possono essere salvati su un file, per esempio aerodata.dat, e caricati
in Matlab con il comando load. In questo esempio, in Matlab viene creata una matrice
aerodata di dimensioni 10  3:
>> load aerodata.dat
>> whos
Name
aerodata

Size
10x3

Bytes
30

Class
double array

Grand total is 30 elements using 240 bytes

Il salvataggio di dati possibile specificando un formato con il comando save. Un


file di dati come aerodata.dat potrebbe essere stato prodotto a valle di una sessione di
lavoro con la sequenza di comandi:

>>
>>
>>
>>
>>

save
save
save
save
save

aerodata.dat
aerodata.dat
aerodata.dat
aerodata.dat
aerodata.dat

dati
x y
dati -ascii
dati -double
dati -ascii -tabs

%
%
%
%
%
%

salva in forma binaria


salva le matrici riga x e y
salva in forma ascii 8 bit
salva in forma ascii 16 bit
salva in forma ascii
con tabulatori

Se il nome del file stdio loutput inviato allo standard output device, generalmente
lo schermo.
Esistono comandi di input/output alternativi e di pi basso livello rispetto a quelli appena richiamati. Ad esempio i comandi dlmread e dlmwrite lavorano su files ASCII in
dui i dati vengono separati da cartteri particolari detti delimiters. Non raro dover manipolare ad esempio files contenenti comma separated values, valori separati da virgole,
Appunti di programmazione a oggetti in C++ e Matlab

DRAFT

I possibili usi del comando di salvataggio sono i seguenti:

ver.2009.d Copyright A. De Marco

>> id = 1:10;
>> x = [2.000 0.2500 0.0740 0.0310 0.0160 ...
0.0090 0.0050 0.0030 0.0020 0.0020];
>> y = [-5.0 -9.1 -23.0 -53.2 -105.1 ...
-185.5 -299.4 -453.7 -653.0 -905.3];
>> dati = [id x y];
>> save aerodata.dat dati -ascii % salva in forma ascii 8 bit

92

Capitolo 4 Una panoramica su Matlab

tipicamente di estensione .csv. Un simile file potr essere letto ed immagazzinato in


una matrice con il comando:
>> A = dlmread(dati.dat,,) % legge il file delimitato da ,

Utilizzatori esperti possono a volte preferire metodi di lettura e scrittura che utilizzano
tutta una serie di comandi derivati e simili a quelli del linguaggio C. Questi comandi sono
riportati nella Tabella 4.1 e si rimanda allhelp per approfondimenti ed esempi pratici
sullargomento.
Tabella 4.1 Comandi di Input/Output di basso livello. Si consulti lhelp di Matlab per

maggiori dettagli sulla sintassi e per degli esempi duso.


fopen, fclose

apertura e chiusura di un file

fread, fwrite

I/O per dati non formattati

fscanf, fprintf

I/O per dati formattati


(formattazione con i comandi simili al C)

fgetl, fgets

I/O dati formattati

ferror, fseek, ftell, frewind

Come i corrispondenti comandi in C

sprintf, sscanf

Conversione di stringhe

Qui si riporta un semplice esempio in cui si scrivono in un file di testo una riga di
intestazione e due colonne di valori, una contenente quelli della variabile indipendente t
ed una contenente quelli della funzione y.t/ D 2 sin.t/ cos.t/:

Noto il metodo ed il formato con cui i dati sono stati scritti nel file valori-funzione.txt,
sar possibile rileggerne il contenuto con comandi analoghi di e con luso di fscanf.
Per finire si ricorda che le Matlab dispone di uno strumento di alto livello per limportazione dei dati chiamato Import Wizard al quale si accede dal menu File|Import Data.
Si tratta di una interfaccia grafica che permette di selezionare il file che contiene i dati, di
analizzarne il contenuto, selezionare il carattere di delimitazione dei dati, escludere eventualmente un certo numero de righe iniziali ed infine di stabilire in quante e quali variabili
caricare i dati nel workspace.

DRAFT

ver.2009.d Copyright A. De Marco

t = linspace(0,2*pi,40); %
40 punti in 0; 2
y = 2*sin(t).*cos(t); %
genera y.t/ D 2 sin.t/ cos.t/
data = [t ; y]; % matrice 2x40
fid = fopen(valori-funzione.txt,w); % "apre" il canale del file
% in scrittura
fprintf(fid,Valori della funzione sin(t)*cos(t) tra 0 e 2*pi\n\n);
fprintf(fid,%4.2f %10.6f\n,data); % tipica formattazione C
% N.B.: questa formattazione permette di ottenere
%
un file con i dati su due colonne
fclose(fid) % "chiude" il canale del file

A. De Marco

Capitolo 5
Programmazione a oggetti in Matlab
I have always wished that my computer would be as easy to use as my telephone.
My wish has come true. I no longer know how to use my telephone!
Bjarne Stroustrup

Indice
5.1
5.2
5.3

Strutture dati di Matlab . . . . . . . . . . . . . . . . . . . . . . . . . 93


Classi e oggetti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Esempi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

[capitolo incompleto]
Questa seconda parte di documento, seppur gi concepita, ancora in via di scrittura.
Lo scopo fondamentale quello di presentare le caratteristiche del linguaggio introdotte a partire dalla versione 2008a che permettono ai programmatori di definire nuove
classi e di derivare tipi da alcune classi predefinite. Il vantaggio di poter programmare a
oggetti in Matlab ovvio: si possono progettare programmi, dai pi semplici ai pi complessi, seguendo il paradigma di programmazione a oggetti servendosi, allo stesso tempo,
dellenorme patrimonio di funzioni e toolbox disponibili.

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Ut purus elit, vestibulum ut,
placerat ac, adipiscing vitae, felis. Curabitur dictum gravida mauris. Nam arcu libero,
nonummy eget, consectetuer id, vulputate a, magna. Donec vehicula augue eu neque.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Mauris ut leo. Cras viverra metus rhoncus sem. Nulla et lectus vestibulum urna
fringilla ultrices. Phasellus eu tellus sit amet tortor gravida placerat. Integer sapien est,
iaculis in, pretium quis, viverra ac, nunc. Praesent eget sem vel leo ultrices bibendum.
Aenean faucibus. Morbi dolor nulla, malesuada eu, pulvinar at, mollis ac, nulla. Curabitur auctor semper nulla. Donec varius orci eget risus. Duis nibh mi, congue eu, accumsan
eleifend, sagittis quis, diam. Duis eget orci sit amet orci dignissim rutrum.
Nam dui ligula, fringilla a, euismod sodales, sollicitudin vel, wisi. Morbi auctor lorem
non justo. Nam lacus libero, pretium at, lobortis vitae, ultricies et, tellus. Donec aliquet,
tortor sed accumsan bibendum, erat ligula aliquet magna, vitae ornare odio metus a mi.

ver.2009.d Copyright A. De Marco

Strutture dati di Matlab

DRAFT

5.1

94

Capitolo 5 Programmazione a oggetti in Matlab

Morbi ac orci et nisl hendrerit mollis. Suspendisse ut massa. Cras nec ante. Pellentesque
a nulla. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus
mus. Aliquam tincidunt urna. Nulla ullamcorper vestibulum turpis. Pellentesque cursus
luctus mauris.

5.2

Classi e oggetti (in Matlab  2008a )

Nulla malesuada porttitor diam. Donec felis erat, congue non, volutpat at, tincidunt tristique, libero. Vivamus viverra fermentum felis. Donec nonummy pellentesque ante.
Phasellus adipiscing semper elit. Proin fermentum massa ac quam. Sed diam turpis, molestie vitae, placerat a, molestie nec, leo. Maecenas lacinia. Nam ipsum ligula, eleifend
at, accumsan nec, suscipit a, ipsum. Morbi blandit ligula feugiat magna. Nunc eleifend
consequat lorem. Sed lacinia nulla vitae enim. Pellentesque tincidunt purus vel magna.
Integer non enim. Praesent euismod nunc eu purus. Donec bibendum quam in tellus.
Nullam cursus pulvinar lectus. Donec et mi. Nam vulputate metus eu enim. Vestibulum
pellentesque felis eu massa.
Quisque ullamcorper placerat ipsum. Cras nibh. Morbi vel justo vitae lacus tincidunt
ultrices. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. In hac habitasse platea
dictumst. Integer tempus convallis augue. Etiam facilisis. Nunc elementum fermentum
wisi. Aenean placerat. Ut imperdiet, enim sed gravida sollicitudin, felis odio placerat
quam, ac pulvinar elit purus eget enim. Nunc vitae tortor. Proin tempus nibh sit amet nisl.
Vivamus quis tortor vitae risus porta vehicula.

Esempi

Fusce mauris. Vestibulum luctus nibh at lectus. Sed bibendum, nulla a faucibus semper,
leo velit ultricies tellus, ac venenatis arcu wisi vel nisl. Vestibulum diam. Aliquam pellentesque, augue quis sagittis posuere, turpis lacus congue quam, in hendrerit risus eros eget
felis. Maecenas eget erat in sapien mattis porttitor. Vestibulum porttitor. Nulla facilisi.
Sed a turpis eu lacus commodo facilisis. Morbi fringilla, wisi in dignissim interdum, justo
lectus sagittis dui, et vehicula libero dui cursus dui. Mauris tempor ligula sed lacus. Duis
cursus enim ut augue. Cras ac magna. Cras nulla. Nulla egestas. Curabitur a leo. Quisque
egestas wisi eget nunc. Nam feugiat lacus vel est. Curabitur consectetuer.
Suspendisse vel felis. Ut lorem lorem, interdum eu, tincidunt sit amet, laoreet vitae,
arcu. Aenean faucibus pede eu ante. Praesent enim elit, rutrum at, molestie non, nonummy vel, nisl. Ut lectus eros, malesuada sit amet, fermentum eu, sodales cursus, magna.
Donec eu purus. Quisque vehicula, urna sed ultricies auctor, pede lorem egestas dui, et
convallis elit erat sed nulla. Donec luctus. Curabitur et nunc. Aliquam dolor odio, commodo pretium, ultricies non, pharetra in, velit. Integer arcu est, nonummy in, fermentum
faucibus, egestas vel, odio.

DRAFT

ver.2009.d Copyright A. De Marco

5.3

A. De Marco

Bibliografia e sitografia
[1] B. Stroustrup, C++. Linguaggio, libreria standard, principi di programmazione.
Addison-Wesley, Pearson Education Italia (collana Professionale), Terza edizione,
2000.
[2] B. Stroustrup, The C++ Programming Language . Addison-Wesley, Special 3rd
Edition, 2000.
[3] A. Koenig, B. E. Moo, Accelerated C++. Addison-Wesley, 2000.
[4] H. Schildt, C++. La guida completa. McGraw-Hill, 2003.
[5] H. Schildt, Herb Schildts C++ Programming Cookbook. McGraw-Hill, 2008.
[6] B. Stroustrup, Sito internet di Bjarne Stroustrup,
http://www.research.att.com/~bs/

[7] N. M. Josuttis, Object-Oriented Programming in C++. John Wiley, 2003.


Sito internet: http://www.josuttis.com/cppbook/cppbook.html
[8] D. Vandevoorde, N. M. Josuttis, C++ Templates. Addison-Wesley, 2003.
[9] N. M. Josuttis, The C++ Standard Library A Tutorial and Reference, Self published, 1999.
Sito internet: http://www.josuttis.com/libbook/

http://www.bo.cnr.it/corsi-di-informatica/corsoCstandard/Lezioni/01Indice.html

[12] C. Olson, sito internet di FlightGear Flight Simulator,


http://www.flightgear.org

[13] C. Olson, sito internet di ispezione interattiva del codice sorgente di FlightGear
Flight Simulator (Interactive CVS log browser),
http://cvs.flightgear.org/viewvc/source/

[14] J. S. Berndt, sito internet di JSBSim, Flight Dynamics Model,


http://www.jsbsim.org

DRAFT

[11] A. Ficarra, M. Murgia, Il linguaggio C++ standard, Corso di formazione ISAC,


CNR, Bologna 2003.
Sito internet:

ver.2009.d Copyright A. De Marco

[10] Autori anonimi, Dal C al C++, Wikibook.


Sito internet: http://it.wikibooks.org/wiki/C%2B%2B

96

Capitolo 5 Bibliografia e sitografia

[15] J. S. Berndt, sito internet di ispezione interattiva del codice sorgente di JSBSim
Flight Dynamics Model (Interactive CVS log browser),
http://jsbsim.cvs.sourceforge.net/viewvc/jsbsim/JSBSim/

[16] J. S. Berndt, JSBSim: An Open Source Flight Dynamics Model in C++. AIAA
Modeling and Simulation Technology Conference, Providence, Rhode Island, 16-19
August 16, 2004.
[17] A. De Marco, E. L. Duke, J. S. Berndt, A General Solution to the Aircraft Trim
Problem. AIAA Paper 2007-6703, AIAA Modeling and Simulation Technology
Conference, Hilton Head, South Carolina, USA, 20-23 August 2007.
[18] D. P. Coiro, A. De Marco, F. Nicolosi, A 6DOF Flight Simulation Environment for
General Aviation Aircraft with Control Loading Reproduction. AIAA Paper 20076364, AIAA Modeling and Simulation Technology Conference, Hilton Head, South
Carolina, USA, 20-23 August 2007.
[19] D. P. Coiro, A. De Marco, F. Nicolosi, The Flight Simulation Environment of
The University of Naples. Proceedings of ISC 2006 (International Simulation
Conference), July 2006, Palermo, Italy.
[20] M. Cooper, Advanced Bash-Scripting Guide. An in-depth exploration of the art of
shell scripting. Disponibile allindirizzo: http://tldp.org/LDP/abs/html/
[21] Sito internet della distibuzione di software libero GNU: http://www.gnu.org
[22] Sito internet del progetto GNU/Linux:
http://www.gnu.org/gnu/linux-and-gnu.it.html

[23] Sito internet del manuale online di GNU make:


http://www.gnu.org/software/make/manual/

[24] Sito internet della distibuzione di software libero Cygwin:

http://www.cygwin.com

[25] Sito internet del sistema MinGW (Minimalist GNU for Windows):

DRAFT

ver.2009.d Copyright A. De Marco

http://www.mingw.org

[26] Sito internet di Borland, produttore del sistema di sviluppo Borland C++Builder:
http://www.borland.com

[27] Sito internet dellambiente di sviluppo integrato Microsoft Visual C++ Express:
http://www.microsoft.com/express/vc/

[28] Sito internet dellambiente di sviluppo integrato Code::Blocks (The open source,
cross platform, free C++ IDE): http://www.codeblocks.org
[29] Sito internet della Bloodshed Software e dellambiente di sviluppo integrato
Dev-C++: http://www.bloodshed.net/devcpp.html
[30] Sito internet dellambiente di sviluppo integrato wxDev-C++ (estensione di
Dev-C++ preconfigurata per lo sviluppo di applicazioni ad interfaccia grafica
basata sulla libreria wxWidgets): http://wxdsgn.sourceforge.net
A. De Marco

97

[31] Sito internet del toolkit grafico wxWidgets (The cross-platform GUI library):
http://wxwidgets.org

[32] Sito internet di Silicon Graphics Inc., produttore del sistema operativo Irix:
http://www.sgi.com

[33] Sito internet del sistema di compilazione multipiattaforma CMake:


http://www.cmake.org

[34] Sito internet delleditore di testi gratuito Notepad++:


http://notepad-plus.sourceforge.net

[35] Sito internet delleditore di testi gratuito Vim (Vi Improved):


[36] The Mathworks, Matlab online documentation:

http://www.vim.org

http:

//www.mathworks.com/access/helpdesk/help/techdoc/index.html?/access/

(versioni 2008b e 2009a).


[37] A. H. Register, A Guide to MATLAB Object-Oriented Programming. Chapman &
Hall/CRC, SciTech Publishing Inc., 2007.
[38] B. W. McCormick, Aerodynamics, Aeronautics, and Flight Mechanics. John Wiley
& Sons, New York, 1979.

DRAFT

ver.2009.d Copyright A. De Marco

[39] J. Katz, A. Plotkin, Low-Speed Aerodynamics. Cambridge University Press, 2nd


edition, Cambridge, England, U.K., 2001.

Appunti di programmazione a oggetti in C++ e Matlab

You might also like