Elementi di
programmazione
ad oggetti
a. a. 2009/2010
Corso di Laurea Magistrale in Ingegneria Elettronica
Docente: Mauro Mazzieri, Dipartimento di Ingegneria Informatica,
Gestionale e dell’Automazione
Lezione 2
Regole di visibilità e ciclo di vita
Visibilità di una variabile
Il campo d’azione o spazio di visibilità
di una variabile (scope) indica la parte
di programma che può accedere alla
variabile per leggerla o modificarla
La visibilità è limitata al blocco
(racchiuso tra parentesi graffe) in cui
la variabile è dichiarata
Visibilità in C++
Il C++ supporta tre tipi di visibilità:
Locale
La
parte di programma che costituisce la
definizione di una funzione definisce un
campo di visibilità separato
Di namespace
La
parte di programma non contenuta nella
dichiarazione o definizione di una funzione o
di una classe
Lo scope del namespace più esterno è detto
scope globale
Di classe
Interno
alla definizione di una classe
Esempio
// scope
int a1 =
int a2 =
int b1 =
int b2 =
globale
3;
4;
9;
11;
double distanza(int x1, int y1, int x2, int y2) {
// scope locale
double dx = x2 - x1;
double dy = y2 - y1;
return sqrt(dx*dx + dy*dy);
}
int main()
{
std::cout << distanza(a1, a2, b1, b2) <<std::endl;
system("pause");
return 0;
}
Esempio: errori e precisazioni
double distanza(int x1,
double dx = a2 – a1;
double dy = b1 - y1;
return sqrt(dx*dx +
}
int y1, int x2, int b1) {
// errore: non è accessibile
// parametro, non b1 del main
dy*dy);
int main()
{
int a1 = 3;
int a2 = 4;
int b1 = 9;
int b2 = 11;
std::cout << distanza(a1, a2, b1, b2) <<std::endl;
system("pause");
return 0;
}
Ciclo di vita
Il ciclo di vita di una variabile è il
periodo compreso tra la sua creazione
e la sua eliminazione
Nasce quando la variabile viene
associata ad una locazione di memoria
Muore quando la locazione di memoria
ritorna libera e riutilizzabile
La vita di una variabile inizia quando
viene dichiarata e finisce con la
parentesi graffa di chiusura del suo
blocco di visibilità
Esempio
int main() {
int a = 5; // a nasce
if (a > 3) {
int b = 7; // b nasce;
a = b * b;
a++;
} // b muore
cout << a;
cout << b; // errore: b non è pù
visibile
} // a muore
Ciclo di vita e scope
Se una variabile è viva, non vuol dire
che sia visibile dalla porzione di
codice in esecuzione ad un dato
istante
Una variabile locale può esistere in
più istanze contemporaneamente
Tutte le istanze sono vive, ma solo una è
visibile
Esempio
int fattoriale(int n) {
int f1 = 1;
if (n > 1)
f1 = fattoriale(n - 1);
return f1 * n;
}
int main()
{
int a = 3;
cout << fattoriale(4);
}
Dentro fattoriale, a è viva ma non visibile
Vengono successivamente create 3 istanze di f1
(corrispondenti alle chiamate ricorsive di fattoriale)
Oggetti e funzioni globali
Una funzione dichiarata nel campo
d’azione globale è una funzione
globale
Una variabile dichiarata nel campo
d’azione globale è un oggetto globale
Il ciclo di vita di un oggetto globale inizia
all’avvio del programma e termina alla
fine del programma
One Definition Rule (ODR): funzioni e
oggetti globali possono essere definiti
una sola volta
Definizioni
La definizione di una funzione le fornisce un corpo,
racchiuso tra parentesi graffe
int calcola(int a, int b); // solo
dichiarazione
int maggiore(int x, int y) { // definizione
if (x > y)
return x;
return y;
}
La definizione di un oggetto ha due possibili forme
<tipo> <nome>; // senza inizializzazione
<tipo> <nome> = <inizializzazione>;
Oggetti locali
La dichiarazione di una variabile nel campo
d’azione locale introduce un oggetto locale
Ci sono 3 generi di oggetti locali:
Automatici
register
Vivono dalla chiamata della funzione in cui sono
definiti fino al ritorno dalla funzione
Oggetti automatici per i quali viene richiesta al
compilatore una memorizzazione veloce
Statici
Risiedono in uno spazio di memoria che rimane
riservato per l’intera vita del programma
Oggetti automatici
Occupano uno spazio di memoria che
viene allocato al momento della
chiamata della funzione in cui sono
dichiarati
Se non vengono inizializzati, hanno
un contenuto che dipende da cosa
era memorizzato in precedenza in
quella locazione di memoria
Lo spazio di memoria a loro dedicato
viene liberato in automatico al
termine della funzione
Oggetti register
Dichiarati con la parola chiave
register
La dichiarazione suggerisce al
compilatore di ottimizzarne l’accesso per
uso frequente
Nella maggior parte dei compilatori
moderni, è inutile e ridondante
Oggetti locali statici
Dichiarate con la parola chiave static
Durano l’intera esecuzione del programma
Sono inizializzate la prima volta che
l’esecuzione del programma passa
attraverso la loro dichiarazione
Se non inizializzate esplicitamente, vengono
inizializzate a 0
Vengono usate quando il valore di una
variabile non deve andar perso alla fine
dell’esecuzione di una funzione ma deve
poter essere riutilizzabile
Esempio
int uso_frequente(int a) {
static int count;
count++;
if (count == 1000)
std::cout <<
"Complimenti, lei è il
millesimo cliente!" <<
std::endl;
return a * 3;
}
Ciclo di vita e visibilità delle
variabili statiche
Una variabile statica definita
all’interno di una funzione
Ha lo stesso ciclo di vita di una variabile
globale
Viene
distrutta solo quando il programma
termina
Ha la stessa visibilità di una variabile
locale
Definizioni di namespace
I namespace (compreso il namespace
globale) possono contenere altri
namespace annidati
Le entità definite in un namespace
sono membre del namespace
I nomi dei membri del namespace sono
composti da
<nome_namespace>::<nome_membro>
:: è l’operatore di scope
Ci si può riferire ai membri del namespace globale
con ::<nome_membro>
Esempi
int a1 = 3;
int f() {
int a = 3;
return ::a1 * a; // uso di una variabile del namespace globale
}
namespace n1 {
int a;
int g() {
// definzione dentro il namespace
return 2;
}
int h();
}
int n1::h() {
// definizione fuori dal namespace
return 3;
}
Uso dei namespace
Generalemente, in un programma si pongono
Le dichiarazioni dei membri di un namespace in un file
header
Le definizioni dei membri in un file di implementazione
Si possono utilizzare i mebri di un namespace
includendo l’header e utilizzando l’operatore di
scope
#include <iostream>
std::cout << “test1”;
Si può rendere visibile un membro di un
namespace con la direttiva using
#include <iostream>
using std::cout;
cout << “test2”;
Uso dei namespcae (cont.)
Si possono rendere visibili tutti i membri di
un namespace con a direttiva using
namespace:
#include <iostream>
using namespace std;
cout << “test3”;
Si può utilizzare un namespace tramite un
alias
#include <iostream>
namespace predefinito = std;
predefinito::cout << “test3”;