Professional Documents
Culture Documents
Maggio 2009
ver. 2009.d
DRAFT
Appunti di programmazione
a oggetti in C++ e Matlab
DRAFT
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
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
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
77
.
.
.
.
.
.
.
.
.
.
79
79
80
81
84
84
84
86
87
88
91
93
93
94
94
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
DRAFT
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
Introduzione
Napoli
aprile 2009
DRAFT
A. De Marco
DRAFT
Appunti di programmazione
a oggetti in C++ e Matlab
DRAFT
Parte I
DRAFT
Introduzione alla
programmazione a oggetti
DRAFT
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
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
DRAFT
* 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
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
Parametri di scambio
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
DRAFT
REAL
x1 =
CALL
x3 =
13
14
DRAFT
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
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;
* 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
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.
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
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!
DRAFT
int main() { }
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
17
1.2.4
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
18
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
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
19
1.3.1
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
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
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
}
void setAlphaDeg(double val){ alpha = val/57.3; }
void setMach(double val){ mach = val; }
void calculate(void)
{
DRAFT
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
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
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 ;
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
DRAFT
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
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
// 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
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
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
24
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;
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
25
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
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
DRAFT
1.4
26
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.
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)
DRAFT
};
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
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);
// ...
};
Funzioni membro
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
* 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
DRAFT
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
29
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?)
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
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
DRAFT
31
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.
DRAFT
32
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
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:
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
33
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
DRAFT
// 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
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
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
DRAFT
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
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 .
DRAFT
38
vorticit`a
aderente
vorticit`a
libera
centroidi
i
i C1
punti di
controllo
V1
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
Figura 1.1 Due possibili modelli per due diverse implementazioni di classi derivate da Wing.
39
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
40
La classe derivata
una (is a) classe base
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
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
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
1.5.1
DRAFT
1.5
41
42
Figura 1.2 Possibili sottoinsiemi della configurazione aerodinamica di un velivolo. Essa pu es-
DRAFT
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
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
DRAFT
44
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
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
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
}
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
DRAFT
1.5.2
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
48
DRAFT
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
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
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
// a vector of pointers
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
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
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
52
2.1
Case sensitivity
Moduli funzione
2.1.1
La funzione main
Passaggio di parametri
da riga di comando e
codici di terminazione
dei programmi
Elementi di base
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
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
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[]){
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"}
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;
oppure come:
a = i++ + j;
DRAFT
Commenti
Lo stesso programma riportato con laggiunta di commenti che spiegano i diversi elementi formali previsti dal linguaggio:
A. De Marco
55
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).
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
2.1.4
DRAFT
2.1.3
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
DRAFT
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
57
Le direttive condizionali
hespressione 1i
hblocco di direttive e/o istruzionii
#elif hespressione 2i
hblocco di direttive e/o istruzionii
#if
#else
#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)
#define
hidentificatorei
Al posto di
#if defined(hidentificatorei)
si pu usare la forma:
#ifdef
hidentificatorei
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
hidentificatorei
DRAFT
#ifndef
58
// 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
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
59
2.2
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).
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
DRAFT
2.2.1
60
La parola chiave
typedef
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
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
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
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).
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.
} ;
#endif
int a = davide.anni; //
//
giovanni.anni = 1;
//
//
Loperatore . (punto)
DRAFT
62
2.2.3
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
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
...
<fstream>
<cstdio>
<string>
<vector>
<cmath>
<iostream>
DRAFT
#include
#include
#include
#include
#include
#include
//------------------------------// File:
DataFile.h
// Author: Agostino De Marco
// Date:
April 2009
//------------------------------#ifndef DATAFILE_H
#define DATAFILE_H
64
namespace ADM {
class DataFile {
public:
DataFile();
~DataFile();
DataFile(string fname);
DRAFT
A. De Marco
2.5 Esempi
65
#endif
<iostream>
<fstream>
<cstdio>
<cstdlib>
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
int numvars
= df.GetNumFields();
int numpoints = df.GetNumRecords();
double starttime = df.GetStartTime();
double endtime
= df.GetEndTime();
66
2) Q (deg/s)
4) P dot (deg/s^2)
3) R (deg/s)
DRAFT
2.6
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
67
DRAFT
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.
DRAFT
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
[capitolo incompleto]
3.1
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
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
3.1.2
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
3.1.3
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
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
Makefile
3.2
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].
>> make
72
Figura 3.2 Schermata di una sessione di lavoro con Microsoft Visual C++ 2008 Express [27].
DRAFT
3.2.1
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
DRAFT
Figura 3.3 La finestra di esplorazione della solution in Microsoft Visual C++ 2008 Express.
73
74
Figura 3.4 La finestra di configurazione delle propriet di un progetto in Microsoft Visual C++
DRAFT
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
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).
DRAFT
Figura 3.7 Finestra di dialogo attraverso la quale possibile modificare il percorso dellOutput
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
Figura 3.8 La finestra di output (o di log) della compilazione e del collegamento (Menu Project,
DRAFT
A. De Marco
Parte II
DRAFT
Programmazione a oggetti
con Matlab
DRAFT
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.
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
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
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
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
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
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
83
DRAFT
Figura 4.3 Una finestra grafica di Matlab ottenuta con il semplice comando plot(0:0.1:1).
84
4.4
Usare lhelp
DRAFT
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.
4.5
4.5.1
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
>> b=pippo
b =
pippo
>> c=23;
>> 12.5
ans =
12.5000
>> whos
Name
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
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.
86
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];
DRAFT
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
87
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
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
88
DRAFT
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
89
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:
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
>>[<out1 >, ... , <outn >] = <nome-funzione>(<in1 >, ... , <inm >);
90
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
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:
>>
>>
>>
>>
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
91
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
>>
>>
>>
>>
>>
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
%
%
%
%
%
%
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
>> 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
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
fread, fwrite
fscanf, fprintf
fgetl, fgets
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
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
[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.
DRAFT
5.1
94
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
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
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/
http://www.bo.cnr.it/corsi-di-informatica/corsoCstandard/Lezioni/01Indice.html
[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/
DRAFT
96
[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
http://www.cygwin.com
[25] Sito internet del sistema MinGW (Minimalist GNU for Windows):
DRAFT
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
http://www.vim.org
http:
//www.mathworks.com/access/helpdesk/help/techdoc/index.html?/access/
DRAFT