G. Mecca – Università della Basilicata – mecca@unibas.it
Programmazione Procedurale in Linguaggio C++
Sottoprogrammi Parte 4
Programmazione Modulare
versione 2.2
Questo lavoro è concesso in uso secondo i termini di una licenza Creative Commons (vedi ultima pagina)
Sommario
m
Introduzione
m
Diagramma delle Chiamate
m
Regole di Visibilità
ðDati Locali e Dati Globali
m
Compilazione Separata
Sottoprogrammi: Programmazione Modulare >> Sommario
G. Mecca - Programmazione Procedurale in Linguaggio C++ 3
Introduzione
m
Programmazione modulare
ðun’applicazione è fatta di una serie di moduli ðprogramma principale (main)
ðsottoprogrammi (procedure e funzioni)
m
Funzionamento
ðsi inizia con l’esecuzione del “main”
ði vari moduli si chiamano a vicenda ðcomunicando attraverso i parametri
Sottoprogrammi: Programmazione Modulare >> Introduzione
4
Introduzione
m
Nella lezione precedente
ðaspetti “tecnologici” della programmazione modulare
ðcosa offre la tecnologia dei sottoprogrammi
m
In questa lezione
ðaspetti “metodologici” legati alla programmazione modulare
ðcome è opportuno utilizzare i sottoprogrammi
Sottoprogrammi: Programmazione Modulare >> Introduzione
G. Mecca - Programmazione Procedurale in Linguaggio C++ 5
Introduzione
m
Aspetti metodologici
ða) albero delle chiamateðb) regole di visibilità e utilizzo dei dati
ðc) compilazione separata e “riuso” del codice
m
Successivamente
ðd) metodologia di sviluppo per raffinamenti successivi
ðe) linee guida per la scrittura di codice di qualità
Sottoprogrammi: Programmazione Modulare >> Introduzione
Diagramma delle Chiamate
m
In un programma modulare
ði moduli dipendono gli uni dagli altri
ðper ogni chiamata il chiamante “dipende” dal chiamato
m
Diagramma delle chiamate
ðdiagramma che rappresenta le relazioni di dipendenza tra i moduli
ðogni modulo è un elemento
ðgli archi descrivono le dipendenze
Sottoprogrammi: Programmazione Modulare >> Diagramma delle Chiamate
G. Mecca - Programmazione Procedurale in Linguaggio C++ 7
Esempio: Equazioni di II Grado
Sottoprogrammi: Programmazione Modulare >> Diagramma delle Chiamate
main
procedura leggiEquazione
funzione primaRadice
funzione discriminante
funzione secondaRadice
procedura stampaRadici
8
Diagramma delle Chiamate
m
Perché è importante
ðaiuta a ragionare sul livello di
“accoppiamento” tra i moduli
ðe sulla logica applicativa del programma
m
Livello di accoppiamento di un modulo
ðnumero di moduli del programma da cui ilmodulo dipende
ðcioè che chiama direttamente o indirettamente
Sottoprogrammi: Programmazione Modulare >> Diagramma delle Chiamate
G. Mecca - Programmazione Procedurale in Linguaggio C++ 9
Esempio: La Morra Cinese
Sottoprogrammi: Programmazione Modulare >> Diagramma delle Chiamate
main
inizioGioco gioca
schermo MenuPrincipale
schermoFinale
schermoIniziale nuovaPartita schermoVittorie
schermo
MenuPartita nuovaMano schermo EsitoPartita
genera
GiocataComputer controllaEsito schermo EsitoMano
Diagramma delle Chiamate
m
Attenzione al livello di accoppiamento
ðtanto più è alto il livello di accoppiamento diun modulo, tanto più il modulo dipende da altri moduli
ðse uno o più degli altri moduli è scorretto, il modulo si comporta scorrettamente
ðse uno o più degli altri moduli cambiano prototipo, bisogna cambiare anche il modulo ðsi tratta di fenomeni negativi
Sottoprogrammi: Programmazione Modulare >> Diagramma delle Chiamate
G. Mecca - Programmazione Procedurale in Linguaggio C++ 11
Diagramma delle Chiamate
m
Tipicamente
ðil main dipende da tutti gli altri moduli
m
In generale, però
ðè importante che il diagramma sia
organizzato per livelli (vedi metodologia) ði moduli a livello più alto dipendono da quelli
a livello più basso
ðai livelli più bassi l’accoppiamento deve diminuire progressivamente
Sottoprogrammi: Programmazione Modulare >> Diagramma delle Chiamate
12
Diagramma delle Chiamate
Sottoprogrammi: Programmazione Modulare >> Diagramma delle Chiamate
main
modulo A
modulo B
modulo C
modulo D
modulo E In questo caso il diagramma delle chiamate
è molto “disordinato”, e i moduli sono tutti fortemente accoppiati; è un sintomo di logica applicativa confusa
G. Mecca - Programmazione Procedurale in Linguaggio C++ 13
Diagramma delle Chiamate
m
Inoltre
ðsi tratta di un ottimo strumento per studiare il funzionamento di un programma scritto da altri
m
Metodo
ðcostruire il diagramma (specificando anche i parametri dei sottoprogrammi)
ðseguire il diagramma per studiare il funzionamento
Sottoprogrammi: Programmazione Modulare >> Diagramma delle Chiamate
Regole di Visibilità
m
A questo punto
ðpossiamo descrivere le regole di visibilità complete del linguaggio
m
Regole di visibilità
ðinsieme di regole che stabiliscono cosa può essere utilizzato in ciascun modulo
ðquali dati possono essere utilizzati
ðquali altri moduli possono essere utilizzati
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
G. Mecca - Programmazione Procedurale in Linguaggio C++ 15
Regole di Visibilità
m
Utilizzare un dato
ðutilizzare una costante, una variabile o un parametro in una istruzione
ðovvero effettuare un’operazione sul corrispondente spazio nella memoria
m
Utilizzare un sottoprogramma
ðutilizzare una funzione o una procedura ðovvero chiamare il sottoprogramma
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
16
Regole di Visibilità
m
Regola di visibilità n.1
ð“prima dichiarare e poi usare”
ðun dato o un sottoprogramma può essere utilizzato solo se è stato precedentemente dichiarato
m
Significato di “precedentemente”
ðnelle istruzioni che precedono il punto in cui l’oggetto deve essere usato
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
G. Mecca - Programmazione Procedurale in Linguaggio C++ 17
Regole di Visibilità
m
In altri termini
ðil compilatore ha bisogno di conoscere informazioni su cosa viene usato per verificare la correttezza dell’uso
m
Esempio: variabile
ðè necessario verificarne il nome ðè necessario verificarne il tipo
ðla regola enunciata era già scontata
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
Regole di Visibilità
m
Per quanto riguarda i sottoprogrammi
ðil compilatore deve verificare che la chiamata sia corretta
ðdeve verificare il nome
ðdeve verificare il numero ed il tipo dei parametri
ðdeve verificare la natura dei parametri (standard o per riferimento)
ðdeve verificare l’eventuale tipo del risultato
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
G. Mecca - Programmazione Procedurale in Linguaggio C++ 19
Regole di Visibilità
m
Soluzione n. 1
ðl’ordine in cui sono definiti i sottoprogrammi rispecchia l’ordine delle chiamate
m
Soluzione sgradevole
ðobbliga a preoccuparsi dell’ordine di valutazione
ðè innaturale (non rispecchia l’organizzazione del metodo e del diagramma delle chiamate, dal più generale al più particolare)
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
>> equazioni1.cpp
20
Regole di Visibilità
m
Soluzione n. 2
ðle informazioni necessarie sono tutte contenute nell’intestazione (o prototipo) ðviene usato il prototipo per “annunciare” il
sottoprogramma
ðla definizione viene fornita successivamente
m
In questo modo
ðposso organizzare i sottoprogrammi più liberamente
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
>> equazioni2.cpp
G. Mecca - Programmazione Procedurale in Linguaggio C++ 21
Regole di Visibilità
m
Prototipo di un sottoprogramma
ðintestazione del sottoprogramma seguita da ; ðserve ad annunciare l’esistenza del
sottoprogramma al compilatore
ðdescrive il modo in cui può essere utilizzato
m
ATTENZIONE
ðdeve esserci il ; finale
ðsuccessivamente deve comparire la definizione completa del sottoprogramma
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
ATTENZIONE alla sintassi per la definizione dei prototipi
Regole di Visibilità
m
La seconda regola di visibilità
ðriguarda i dati visibili in ciascun modulo di programma
m
Prima di enunciarla
ðdettagli sulle dichiarazioni dei dati ðdati “locali”
ðdati “globali”
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
G. Mecca - Programmazione Procedurale in Linguaggio C++ 23
Dati Locali e Dati Globali
m
Dati locali
ðdati dichiarati all’interno di un modulo ðcorrispondono ad uno spazio di memoria ðcostanti simboliche, variabili, parametri
m
Ogni modulo ha i suoi dati locali
ði dati sono “indipendenti”: i dati di un modulo non sono visibili all’interno di un altro
ðpossono avere nomi uguali
ðcorrispondono a spazi di memoria diversi
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
24
Dati Locali e Dati Globali
void leggiEquazione (float &a, float &b, float &c) { cout << "Inserisci i coefficienti dell'equazione \n";
cin >> a;
cin >> b;
cin >> c;
return;
}
float primaRadice (float a, float b, float c){
float x;
x=(-b+sqrt(discriminante(a,b,c)))/(2*a);
return x;
}
float secondaRadice (float a, float b, float c){
float x;
x=(-b-sqrt(discriminante(a,b,c)))/(2*a);
return x;
}
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
Dati locali -3 parametri per riferimento (a,b,c) Dati locali
-3 parametri standard (a,b,c)
-1 variabile (x)
Dati locali
-3 parametri standard (a,b,c)
-1 variabile (x) Spazi diversi nella memoria
Spazi diversi nella memoria
G. Mecca - Programmazione Procedurale in Linguaggio C++ 25
Dati Locali e Dati Globali
m
Dati globali
ðcostanti e variabili dichiarate al di fuori di tutti i moduli (dichiarate “a livello di file”)
ðsono comuni a tutti i moduli
m
Utilizzo tipico
ðcostanti utilizzate da molti moduli
ðè utile poterle definire una volta per tutte
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
Dati Locali e Dati Globali
const int MANI = 3;
void main() { string nome;
inizioGioco(nome);
gioca(nome);
schermoFinale(nome);
}
void inizioGioco(string& nome) { int seme = time(NULL);
srand(seme);
schermoIniziale(nome);
return;
}
int generaGiocataComputer() { int giocata;
giocata = rand()/100%3 + 1;
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
Dati locali
- 1 variabile (nome) Dati globali - 1 costante (MANI) Dati locali
- 1 parametro per riferimento (nome) - 1 variabile (seme) Dati globali - 1 costante (MANI) Dati locali
- 1 variabile (giocata) Dati globali
G. Mecca - Programmazione Procedurale in Linguaggio C++ 27
Dati Locali e Dati Globali
m
A questo punto
ðè possibile enunciare la regola generale
m
Regola di visibilità n. 2
ðin ciascun modulo sono sempre visibili tutti i dati locali
ði dati globali sono visibili a meno che nel modulo non sia dichiarato un dato locale con lo stesso nome
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
ATTENZIONE alle regole di
visibilità
28
Dati Locali e Dati Globali
const int n = 1000;
float cubo(float numero);
void main() { int numero;
cin >> numero;
if (numero <= n)
cout << cubo(numero);
}
float cubo (float numero) { float n;
n = numero * numero * numero;
return n;
}
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
Dati locali
- 1 variabile intera (numero)
Dati globali
- 1 costante intera (n)
Dati locali
- 1 parametro standard (numero)
- 1 variabile reale (n) In questo modulo NON è visibile la costante n
G. Mecca - Programmazione Procedurale in Linguaggio C++ 29
Dati Locali e Dati Globali
m Variabili globali
ðè possibile definire anche variabili globali, ovvero spazi della memoria utilizzabili in tutti i moduli del programma
m Attenzione, questo fatto ha delle conseguenze
ðconsente ai moduli del programma di comunicare attraverso le variabili globali
ðoltre che attraverso il passaggio dei parametri
ðun modulo può cambiare il valore di una var globale e questo cambiamento è visibile negli altri moduli
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
Dati Locali e Dati Globali
m
Un esempio
ðil parametro “nome” della morra cinese ðne viene acquisito il valore all’inizio
dell’applicazione, e poi viene passato a moltissimi dei sottoprogrammi
ðsembrerebbe un ottimo candidato a diventare una variabile globale
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
>> morraCinese4.cpp
G. Mecca - Programmazione Procedurale in Linguaggio C++ 31
Dati Locali e Dati Globali
m
In questo modo però
ðintroduco un livello molto più alto di accoppiamento tra i sottoprogrammi
ðmodifiche alla variabile globale fatte in un sottoprogramma influenzano il
funzionamento di tutti gli altri
ðesempio: generaGiocataComputer() e i vari schermi in cui viene stampato il nome
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
32
Esempio: La Morra Cinese
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
main
inizioGioco gioca
schermo MenuPrincipale
schermoFinale
schermoIniziale nuovaPartita schermoVittorie
schermo
MenuPartita nuovaMano schermo EsitoPartita
genera
GiocataComputer controllaEsito schermo EsitoMano
>> morraCinese5.cpp generaGiocataComputer
G. Mecca - Programmazione Procedurale in Linguaggio C++ 33
Dati Locali e Dati Globali
m
In sintesi
ðsi tratta di una forma di comunicazione tra i moduli che non usa i parametri
ðaumenta molto il livello di accoppiamento ðse usata sistematicamente rende di difficile
comprensione il codice
m
Metodologicamente
ðnon utilizzeremo MAI variabili globali
ðè opportuno invece utilizzare costanti globali
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
Dati Locali e Dati Globali
m
Riassumendo le linee guida sui dati
ði sottoprogrammi possono utilizzare costanti e variabili locali
ðle funzioni è opportuno che abbiano solo parametri standard
ðle procedure possono avere sia parametri standard che per riferimento
ðè possibile utilizzare costanti globali
ðnon è opportuno utilizzare variabili globali
Sottoprogrammi: Programmazione Modulare >> Regole di Visibilità
G. Mecca - Programmazione Procedurale in Linguaggio C++ 35
Compilazione Separata
m
Organizzazione del codice sorgente
ðpuò essere organizzato in vari file ði file possono essere compilatiseparatamente
ðe poi collegati assieme successivamente
m
Esempio tipico
ðun file per i sottoprogrammi
ðun file per il programma principale
Sottoprogrammi: Programmazione Modulare >> Compilazione Separata
36
Compilazione Separata
m
Per effettuare la compilazione separata
ðnel file del programma devono esserci iprototipi dei sottoprogrammi chiamati
ðsoluzione tipica: definire un file di intestazioni
m
File di intestazioni (“header file”)
ðfile contenente i prototipi dei sottoprogrammi ðtipicamente il file ha estensione .h
ðviene incluso nel programma principale per effettuare correttamente la compilazione
Sottoprogrammi: Programmazione Modulare >> Compilazione Separata
>> morraCinese6.cpp
G. Mecca - Programmazione Procedurale in Linguaggio C++ 37
Compilazione Separata
m Attenzione alla sintassi della #include
m Per le librerie di sistema: parentesi acute
ð #include <iostream.h>
#include <fstream.h>
ðil file .h viene cercato tra le cartelle di sistema (specificate nel file di configurazione)
m Per le librerie dell’utente: virgolette
ð #include “morraCinese6Lib.h”
ðil file .h deve essere nella stessa cartella in cui c’è il file in cui compare la include
Sottoprogrammi: Programmazione Modulare >> Compilazione Separata
Compilazione Separata
m
Semantica della #include
ðha l’effetto di includere letteralmente il contenuto del file specificato all’interno del file in cui compare
ðistruzione per istruzione
m
Esempio: morraCinese6.cpp
ð#include “morraCinese6Lib.h”
ðè come se il file morraCinese6.cpp
contenesse i prototipi dei sottoprogrammi
Sottoprogrammi: Programmazione Modulare >> Compilazione Separata
G. Mecca - Programmazione Procedurale in Linguaggio C++ 39
Compilazione Separata
m
In effetti, nel caso della Morra Cinese
ðquesto meccanismo è del tutto inutile ðuso più interessante: creazione di “librerie”m
Libreria
ðcollezione di sottoprogrammi ðutilizzabili in applicazioni diverse
m
Esempio
ðlibreria per l’analisi di equazioni di II grado
Sottoprogrammi: Programmazione Modulare >> Compilazione Separata
>> equazioni3Lib.cpp
>> equazioni3Lib.h
40
Compilazione Separata
m
Esempio: equazioni3.cpp
ðun’applicazione su equazioni di II grado (main più eventuali altri sottoprogrammi) ðutilizza la libreria equazioni3Lib.cpp
ðinclude il file di intestazioni equazioni3Lib.h ðpuò essere compilato indipendentemente
dalla libreria usando l’opzione –c di bcc32 ðal termine deve essere collegato con il
codice oggetto della libreria
Sottoprogrammi: Programmazione Modulare >> Compilazione Separata
G. Mecca - Programmazione Procedurale in Linguaggio C++ 41
Compilazione Separata
Sottoprogrammi: Programmazione Modulare >> Compilazione Separata
Compilazione programma principale
equazioni3.cpp
Collegamento codice oggetto equazioni3.obj
codice eseguibile equazioni3.exe
#include<iostream.h>
#include”equazioni3Lib.h”
void main() { ...
}
equazioni3Lib.h
void leggiEquazione (float&a, float&b, float&c);
float discriminante (float a, float b, float c);
...
#include<iostream.h>
void leggiEquazione (float&a, float&b, float&c) {
cout << “Coefficienti: ”;
cin >> a;
...
equazioni3Lib.cpp
Compilazione
codice oggetto equazioni3Lib.obj bcc32 -c
bcc32 -c
ilink32 + opzioni complesse In alternativa: posso compilare e collegare contemporaneamente usando solo bcc32
Compilazione Separata
m
Utilizzo del collegatore della Borland
ðcompilo i due file di codice sorgentebcc32 –c equazioni3Lib.cpp bcc32 –c equazioni3.cpp
ðcollego con il comando (complesso !)
ilink32 /Tpe c0x32 equazioni3.obj equazioni3Lib.obj,
equazioni3.exe,,import32 cw32 m
Una soluzione molto più semplice
ðbcc32 equazioni3.cpp equazioni3Lib.cpp
Sottoprogrammi: Programmazione Modulare >> Compilazione Separata
G. Mecca - Programmazione Procedurale in Linguaggio C++ 43
Compilazione Separata
m
Concludendo
ðuna libreria C/C++ è fatta normalmente di due file
ðun file con le intestazioni dei sottoprogrammi ðun file con le definizioni dei sottoprogrammi
m
Terminologia
ðfile di intestazioni: “interfaccia” della libreria ðfile di definizioni: “implementazione” della lib.
Sottoprogrammi: Programmazione Modulare >> Compilazione Separata
44
Compilazione Separata
m
Interfaccia della libreria
ðtutte le informazioni che è necessario conoscere per chiamare i sottoprogrammi
m
Implementazione della libreria
ðtutte le informazioni che è necessario conoscere per eseguire i sottoprogrammi
m
Il programmatore
ðdeve conoscere necessariamente la prima e non la seconda
Sottoprogrammi: Programmazione Modulare >> Compilazione Separata
ATTENZIONE alla differenza tra
interfaccia e implementazione
G. Mecca - Programmazione Procedurale in Linguaggio C++ 45
Compilazione Separata
m
Idealmente
ðun programmatore scrive un’applicazione sulla libreria basandosi sull’interfaccia ðutilizza inizialmente una specifica
implementazione
ðsuccessivamente l’implementazione può essere cambiata (migliorata, corretta) senza che sia necessario cambiare il codice
dell’applicazione scritta (basta ricompilare)
Sottoprogrammi: Programmazione Modulare >> Compilazione Separata
Compilazione Separata
m
Idea alla base di questo meccanismo
ð“riuso” del codice (un “mito” informatico) ðsviluppare il codice è costosoðper quanto possibile, bisognerebbe evitare di sviluppare di nuovo soluzioni già esistenti
m
Utilità delle librerie
ðcollezione di sottoprogrammi di utilizzo sufficientemente generale
ðdi correttezza verificata
Sottoprogrammi: Programmazione Modulare >> Compilazione Separata
G. Mecca - Programmazione Procedurale in Linguaggio C++ 47
Compilazione Separata
m
In generale
ðè importante puntare al riuso del codice
ðè una “filosofia” a cui deve essere improntata tutta l’attività di programmazione
m
In realtà, però
ðil principale limite dei linguaggi procedurali è che non consentono un riuso adeguato
ðanche per questo sono stati soppiantati dai linguaggi orientati agli oggetti
Sottoprogrammi: Programmazione Modulare >> Compilazione Separata
48
Riassumendo
m
Programmazione Modulare
ðapplicazione fatta di moduli che si chiamano a vicenda
m
Aspetti Metodologici
ðdiagramma delle chiamate ðdati locali e globali
ðcompilazione separata ðlibrerie
ðinterfaccia e implementazione
Sottoprogrammi: Programmazione Modulare >> Sommario
G. Mecca - Programmazione Procedurale in Linguaggio C++ 49
Termini della Licenza
m This work is licensed under the Creative Commons Attribution- ShareAlike License. To view a copy of this license, visit
http://creativecommons.org/licenses/by-sa/1.0/ or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
Termini della Licenza
m Questo lavoro viene concesso in uso secondo i termini della licenza “Attribution-ShareAlike” di Creative Commons. Per ottenere una copia della licenza, è possibile visitare
http://creativecommons.org/licenses/by-sa/1.0/ oppure inviare una lettera all’indirizzo Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.