Gestione della memoria
Ilio Catallo – info@iliocatallo.it
Outline
¤ Tempo di vita degli oggetti
¤ Segmentazione della memoria
¤ Oggetti dinamici
¤ Strutture dinamiche
¤ Array dinamici
¤ Array monodimensionali
¤ Array multidimensionali
2
Tempo di vita degli oggetti
3
Oggetti
¤ Una variabile può dunque essere definita come un
oggetto con nome
¤ RICORDA: il termine oggetto può anche essere usato
come sinonimo di istanza di una classe
¤ Le due definizioni non sono equivalenti
Un oggetto è una regione di memoria di un determinato tipo dato
4
Oggetti
¤ Esempio: oggetto senza nome (temporaneo) di tipo int
¤ Esempio: oggetto con nome (variabile) di tipo int
std::cout << (5+3)*3 << std::endl;
int result = (5+3)*3; // ‘result’ is the name of
// an object of type int
std::cout << result << std::endl;
5
Tempo di vita di un oggetto
¤ Ogni oggetto in un programma è caratterizzato da un
proprio tempo di vita (storage duration)
¤ Tempo di vita: l’intervallo di tempo che intercorre tra la
creazione dell’oggetto e la sua distruzione
6
Tempo di vita di un oggetto
¤ Perchè limitare il tempo di vita degli oggetti?
¤ Per preservare spazio in memoria
¤ Esempio: se una funzione non è in esecuzione, non c’è
motivo di mantenere in memoria le corrispettive variabili
locali
7
Tempo di vita di un oggetto
¤ A seconda del suo tempo di vita, un oggetto può essere
classificato come:
¤ Temporaneo
¤ Automatico
¤ Statico
¤ Dinamico
¤ Thread-local
8
Oggetti temporanei
¤ Gli oggetti temporanei contengono risultati intermedi
ottenuti durante la valutazione di un’epressione
¤ Esempio: Il risultato di y*z deve essere salvato da
qualche parte prima di poter essere aggiunto a x
int v = x + y*z;
9
Oggetti temporanei
¤ L’oggetto temporaneo non ha un nome
¤ La loro creazione è invisibile all’utente
¤ Non è possibile assegnargli un valore
¤ L’oggetto viene distrutto al termine della full-expression
che lo contiene
¤ Full-expression: un’espressione che non è contenuta in
un’espressione più grande
int v = x + y*z;
10
Oggetti automatici
¤ Un oggetto automatico viene creato al momento della
sua definizione e distrutto al termine del blocco che lo
contiene
¤ Esempio: la variabile locale x viene distrutta al termine
del blocco, ed è quindi un oggetto automatico
void do_nothing() {
int x = 12;
}
11
Oggetti automatici
¤ Attenzione a non confendere i due concetti:
¤ Il nome della variabile è locale: il nome è visibile
unicamente all’interno del blocco
¤ L’oggetto è automatico: la variabile verrà distrutta al
termine del blocco
una variabile locale è un oggetto automatico
12
Oggetti statici
¤ Un oggetto statico viene creato ad avvio del
programma e distrutto al termine del programma
¤ Esempi di oggetti statici:
¤ Variabili globali
¤ Variabili locali dichiarate come static
13
Oggetti statici
¤ Una variabile globale è una variabile il cui nome è visibile
in ogni punto del programma
¤ La variabile globale x è un oggetto statico, la sua vita
termina al termine del programma
int x = 10;
int main() {
x = 5; // 'x' is a global variable
std::cout << x << std::endl;
}
14
Oggetti statici
¤ Una variabile locale static è inizializzata a zero ad
avvio del programma, e distrutta al termine
dell’applicativo
¤ Esempio: la variabile ctr viene incrementata ad ogni
chiamata di count_calls()
size_t count_calls() {
static size_t ctr;
return ++ctr;
}
15
Oggetti statici
¤ il blocco che contiene una variabile locale static
determina la visibilità della variabile, ma non il suo tempo
di vita
¤ Il nome della variabile è visibile solo all’interno della
funzione, ma…
¤ La variabile viene distrutta al termine del programma, non al
termine della funzione
size_t count_calls() {
static size_t ctr;
return ++ctr;
}
16
Oggetti dinamici
¤ Una volta creato, un oggetto dinamico rimane in
memoria fino a quando non viene esplicitamente
richiesta la sua distruzione
¤ Il tempo di vita di un oggetto dinamico è gestito dal
programmatore, che decide:
¤ Quando creare l’oggetto dinamico
¤ Quando distruggere l’oggetto
17
Oggetti dinamici
int
Il programmatore
crea un oggetto
dinamico di tipo
int
5
int
L’oggetto
dinamico viene
utilizzato
5
int
Il programmatore
distrugge l’oggetto
quando non più
necessario
18
Segmentazione della memoria
19
Processo in memoria
¤ Il sistema operativo (SO) assegna ad ogni programma in
esecuzione (processo) una porzione della memoria
disponibile
¤ Tale memoria permette l’effettiva esecuzione del
programma
¤ Esempio: una parte viene adoperata per mantenere in
memoria il valore delle variabili
20
Processo in memoria
¤ La porzione di memoria dedicata al processo viene
chiamata lo spazio d’indirizzamento (address space) del
processo
memoria assegnata al
programma in esecuzione
1775 3472…
21
Segmentazione
¤ Lo spazio d’indirizzamento di un processo è suddiviso in
quattro aree (segmenti):
¤ Code segment*
¤ Data segment
¤ Heap segment
¤ Stack segment
¤ Ogni segmento viene utilizzato per uno scopo diverso
*anche detto text segment
22
Segmentazione
¤ La classica rappresentazione dello spazio
d’indirizzamento è ottenuta semplicemente ruotando la
figura di 90°
memoria assegnata al
programma in esecuzione
Stack
1775 3472…
HeapDataCode
23
Segmentazione
Stack
Heap
Data
Code
1775
3472
…
memoriaassegnataal
programmainesecuzione
24
Code segment
¤ Il code segment è un’area di memoria a sola lettura che
contiene il codice che verrà eseguito
Stack
Heap
Data
movl $12, -12(%rbp)
movl $13, -16(%rbp)
movl -12(%rbp), %eax
int main() {
int a = 12, b = 13;
int c = a + b;
}
25
Data segment
¤ Il data segment contiene variabili globali e variabili locali
static
Stack
Heap
x: 10
ctr: 0
Code
int x = 10;
size_t count_calls() {
static size_t ctr;
return ++ctr;
}
int main() {
count_calls();
return 0;
}
26
Stack segment
¤ Lo stack mantiene in memoria le variabili locali ed i
parametri in ingresso alle funzioni
int main() {
int a = 15, b = 10;
return 0;
}
a: 15
b: 10
spazio rimanente nello stack
Heap
Data
Code
27
Stack segment
¤ Lo stack è organizzato come una pila, ogni volta che
una funzione viene invocata, viene aggiunto un nuovo
strato
¤ Il nuovo strato (frame) contiene le variabili locali ed i
parametri in ingresso della funzione appena invocata*
¤ Quando una funzione termina, il corrispettivo frame viene
rimosso dalla pila
¤ Le variabili locali vengono automaticamente distrutte
* il frame contiene anche informazioni che
permettono, una volta termina una funzione, che
il controllo ritorni alla funzione chiamante
28
Stack segment
void do_nothing2() {
int y = 13;
}
void do_nothing1() {
int x = 12;
do_nothing2();
}
int main() {
int a = 15, b = 10;
do_nothing1();
return 0;
}
a: 15
b: 10
x: 12
y:13
spazio
rimanente
nello stack
main()
do_nothing1()
do_nothing2()
Frame 1
Frame 2
Frame 3
29
Heap segment
¤ L’heap segment è l’area di memoria che contiene gli
oggetti dinamici
¤ A differenza dello stack, un nuovo oggetto può
occupare una posizione casuale all’interno dell’heap
¤ Conseguenza: non esiste nessun meccanismo che
automaticamente elimini gli oggetti non più utilizzati
Heap
Occupato
Libero
30
Oggetti dinamici
31
Creare oggetti dinamici
¤ Siamo al corrente di un solo modo per creare un oggetto
che sia successivamente modificabile:
¤ Assegnargli un nome, in altre parole definire una variabile
Come possiamo creare un oggetto dinamico?
32
Creare oggetti dinamici
¤ Assegnare un nome ad un oggetto comporta però la
definizione del suo tempo di vita
¤ Variabile locale? oggetto automatico
¤ Variabile locale static? oggetto statico
¤ variabile globale? oggetto statico
Come possiamo creare un oggetto dinamico?
33
Creare oggetti dinamici
¤ Un oggetto dinamico deve quindi essere anonimo, in
altre parole, non deve avere un nome
Come possiamo manipolare un oggetto senza nome?
34
Creare oggetti dinamici
¤ Un oggetto dinamico deve quindi essere anonimo, in
altre parole, non deve avere un nome
¤ Anche se senza nome, l’oggetto dinamico deve avere
un indirizzo
¤ IDEA: usare un puntatore per contenere l’indirizzo
dell’oggetto dinamico
Come possiamo manipolare un oggetto senza nome?
35
Espressione new
¤ L’espressione new permette di creare oggetti dinamici
¤ Notare come:
¤ a sia una variabile locale, dunque un oggetto automatico
¤ l’oggetto int anonimo sia invece un oggetto dinamico
int* a = new int; // 'a' is pointing to an unnamed,
// dynamic object of type int
36
Espressione new
¤ L’espressione new permette di creare oggetti dinamici
¤ Ricorda: la variabile a non è l’oggetto dinamico, è un
puntatore all’oggetto dinamico
int* a = new int; // 'a' is pointing to an unnamed,
// dynamic object of type int
37
Rappresentazione in memoria
Heap
Stack
a: int*
int
Un oggetto dinamico
non ha nome
38
Espressione new
¤ L’espressione new esegue due operazioni:
¤ Invoca l’operatore new, che alloca nell’heap il quantitativo
di memoria necessaria
¤ Costruisce l’oggetto nell’area di memoria designata
¤ L’espressione new restituisce l’indirizzo dell’oggetto
dinamico
int* a = new int;
39
Inizializzare oggetti dinamici
¤ Gli oggetti dinamici sono inizializzati a default
¤ I tipi built-in contengono quindi un valore non noto
¤ Istanze di una classe vengono inizializzati attraverso il
costruttore di default
¤ Come per gli oggetti automatici, è quindi buona norma
inizializzare gli oggetti dinamici con valori sensati
40
Inizializzare oggetti dinamici
¤ Sono possibili diversi tipi di inizializzazione:
¤ Inizializzazione diretta: l’oggetto puntato da d vale 12
¤ Inizializzazione per copia: l’oggetto puntato da c vale 20
¤ Inizializzazione per valore: l’oggetto puntato da e vale 0
int b = 20;
int* d = new int(12); // direct initialization
int* c = new int(b); // copy initialization
int* e = new int(); // value initialization
41
Modificare oggetti dinamici
¤ Per alterare oggetto dinamici si utilizza l’operatore di
indirezione *
int* a = new int;
int b = 12;
*a = b + 7;
std::cout << "the value pointed to by a is ”
<< *a << std::endl;
}
42
Distruggere oggetti dinamici
¤ Gli oggetti dinamici vanno esplicitamente distrutti
¤ Ogni oggetto allocato occupa parte della memoria
disponibile nell’heap
¤ La memoria non è illimitata
¤ È tecnicamente possibile saturare tutto lo spazio a
disposizione
43
Espressione delete
¤ L’espressione delete permette di distruggere un oggetto
dinamico
¤ Dopo la delete, l’oggetto dinamico puntato da a non
esiste più
int* a = new int;
int b = 12;
*a = b + 7;
delete a;
44
Espressione delete
Heap
Stack
a: int*
1
9
int
45
Espressione delete
¤ L’espressione delete esegue due operazioni:
¤ Invoca l’operatore delete, così da distrugge l’oggetto
¤ Dealloca la memoria precedentemente allocata,
rendendola così nuovamente disponibile
delete a;
46
Dangling pointers
¤ L’espressione delete distrugge l’oggetto dinamico
¤ Il corrispettivo puntatore non è quindi più valido
¤ Infatti, l’oggetto puntato non esiste più
¤ Il puntatore continua però a contenere l’indirizzo di dove
una volta si trovava l’oggetto dinamico
47
Dangling pointers
¤ L’espressione delete distrugge l’oggetto dinamico
¤ Il puntatore è diventato un dangling pointer
¤ Dangling pointer: puntatore che punta ad un area di
memoria precedentemente occupata da un oggetto non
più esistente
48
Dangling pointers
¤ La soluzione è quindi rafforzare il fatto che il puntatore
non sta più puntando a nulla
¤ Regola: dopo la distruzione di un oggetto dinamico, il
corrispettivo puntatore deve essere posto a nullptr (o
NULL)
delete a;
a = nullptr;
49
Strutture dinamiche
50
Strutture
¤ Le strutture generalizzano il concetto di array:
¤ Un array è una sequenza di elementi dello stesso tipo
¤ Una struttura è un sequenza di elementi di tipo diversi
51
Una struttura è una sequenza di elementi (membri) di tipo
arbitrario
Strutture
¤ Address è un tipo composto e definito dall’utente
¤ Una volta che il tipo Address è stato definito, è possibile
dichiarare variabili di tipo Address:
52
struct Address {
std::string street;
int number;
std::string town;
};
Address polimi_address;
Inizializzare una struttura
¤ Dal C++11, è possibile inizializzare oggetti di tipo struttura
mediante una lista di inizializzazione*
53
* prima del C++11, ciò era possibile unicamente nel caso in cui
la struttura fosse stata anche un aggregato
Address polimi_address = {"Piazza Leonardo Da Vinci",
32,
"Milan"};
Member access operator
¤ I membri di una struttura sono accessibili mediante
l’operatore . (member access operator)
54
polimi_address.street = "Piazza Leonardo Da Vinci";
polimi_address.number = 32;
polimi_address.town = "Milan”;
Strutture dinamiche
¤ È possibile dichiarare strutture dinamiche mediante
l’espressione new
¤ L’espressione new restituisce l’indirizzo della struttura
dinamica allocata nell’heap
55
Strutture dinamiche
¤ Esempio: la struttura polico_address è inizializzata a
default
56
Address* polico_address = new Address; // default
// initialization
Rappresentazione in memoria
57
Heap
Stack
polico_address: Address*
Un struttura dinamica
non ha nome street:
number:
town:
Address
Inizializzare una struttura dinamica
¤ Dal C++11, una struttura dinamica può essere inizializzata
con una lista di inizializzazione*
58
Address* polico_address = new Address{“Via Valleggio",
11,
”Como"};// uniform
// initialization
Accedere a membri di
una struttura dinamica
¤ Per accedere alla struttura dinamica è necessario:
¤ Accedere alla struttura mediante l’operatore *
¤ Accedere a un particolare membro mediante l’operatore .
59
(*polico_address).street = "Via Valleggio”;
Mediante indirezione
recuperiamo la struttura
Mediante l’operatore di
accesso alteriamo un
particolare membro della
struttura
Accedere a membri di
una struttura dinamica
¤ Per accessi a membri di una struttura mediante
puntatore è disponibile una variante del member access
operator
¤ L’operatore -> è talvolta indicato come
pointer-to-member operator
60
polico_address->street = "Via Valleggio”;
Distruggere una struttura dinamica
¤ Una struttura dinamica si distrugge mediante l’utilizzo
della espressione delete
61
delete polico_address;
Array dinamici
Creare array dinamici
¤ Un array dinamico può essere creato mediante
l’espressione new[]
¤ L’espressione new restituisce l’indirizzo del primo
elemento dell’array (non avviene alcun decadimento)
¤ Nell’esempio, tale indirizzo viene salvato in un puntatore di
nome numbers
int* numbers = new int[5]; // dynamic (hence, unnamed) array
// of 5 elements of type int
63
Creare array dinamici
Heap
Stack
numbers: int*
int[5]
Un array dinamico non
ha nome
(array anonimo)
64
Inizializzare un array dinamico
¤ Dall C++11, un array dinamico può essere inizializzato in
in modo simile ad un comune array*:
¤ Tale modalità di inizializzazione si chiama inizializzazione
uniforme (uniform initialization)
int* numbers = new int[5]{1, 7, 13, 5, 9};
65
*Differentemente dai comuni array è però sempre necessario
specificare la dimensione
Accedere ad un array dinamico
¤ L’accesso ad un array dinamico avviene secondo le
stesse modalità di un comune array
¤ Infatti, come sappiamo l’operatore [] è un operatore
definito sui puntatori
int* numbers = new int[5]{1, 7, 13, 5, 9};
numbers[2] = 20; // implemented as *(numbers + 2) = 20
66
Distruggere un array dinamico
¤ Il programmatore è responsabile della distruzione di array
dinamici non più necessari
¤ L’espressione delete[] distrugge un array dinamico
delete[] numbers;
67
Distruggere un array dinamico
¤ L’uso delle [] è cruciale
¤ È l’unico modo per chiedere al compilatore di distruggere
un array di oggetti, piuttosto che un singolo oggetto
¤ Se non usato, si incappa in un undefined behavior
delete[] numbers;
68
Array di dimensioni variabili
Dimensione di un array
¤ La dimensione di un array automatico deve essere nota
a tempo di compilazione (compile time)
¤ Tale imposizione facilita la gestione della stack segment
constexpr size_t DIM = 5;
int numbers[DIM];
70
Dimensione di un array
vs stack
¤ Invocare una funzione significa impilare un nuovo frame
nello stack segment
void do_nothing() {
int x = 2;
char y[2] ={5, 4};
int c = 3;
}
int main() {
int a = 12;
do_nothing();
return 0;
}
a: 12
x: 2
y[0]: 5
y[1]: 4
c: 3
spazio
rimamente
nello stack
main()
do_nothing()
71
Frame pointer e stack pointer
¤ Durante l’esecuzione di una funzione, possiamo
identificare due indirizzi importanti in memoria
¤ Stack pointer: l’indirizzo dell’ultima cella di memoria valida
nello stack
¤ Frame pointer: l’indirizzo della prima cella di memoria
appartenente al frame attualmente in esecuzione
72
Frame pointer e stack pointer
¤ Se supponiamo che do_nothing() sia in esecuzione, si
ha che:
a: 12
x: 2
y[0]: 5
y[1]: 4
c: 3
spazio
rimamente
nello stack
do_nothing()
stack pointer
frame pointer
main()
73
Frame pointer e stack pointer
¤ Gli indirizzi delle variabili locali della funzione in
esecuzione sono deducibili a partire dall’indirizzo
contenuto nel frame pointer
¤ Dove lo spiazzamento è misurato in byte
indirizzo var. locale = frame pointer + spiazzamento
74
Frame pointer e stack pointer
¤ Se supponiamo che un int occupi 2 byte, e un char 1
byte
a: 12
x: 2
y[0]: 5
y[1]: 4
c: 3
spazio
rimamente
nello stack
stack pointer
frame pointer frame pointer + 0
frame pointer + 2 byte
frame pointer + 3 byte
frame pointer + 4 byte
75
Dimensione di un array
¤ Se richiediamo che le variabili abbiano dimensioni note a
priori tutti gli spiazzamenti sono calcolabili a compile-time
¤ Conseguenza: è possibile conoscere a tempo di
compilazione la dimensione del frame di una funzione
¤ Questo comporta:
¤ Codice più veloce
¤ Maggiore semplicità nella scrittura dei compilatori
76
Array di dimensioni variabili
¤ Non possiamo usare normali array, perchè verrebbero
allocati nello stack, totalmente gestito dal compilatore
¤ Al contrario, l’heap è una porzione di memoria
totalmente a disposizione del programmatore
¤ IDEA: memorizzare l’array di dimensioni variabili
all’interno dell’heap segment
Come definire un array la cui dimensione venga
specificata a tempo d’esecuzione (run time)?
77
Array di dimensioni variabili
¤ Al momento della creazione di un array dinamico, la
dimensione può essere una qualsiasi espressione
¤ Non è richiesto che l’espressione sia costante
¤ Nell’esempio, dim non è una quantità costante, ma può
comunque essere usata per creare un array dinamico
size_t dim = 5;
int* numbers = new int[dim];
78
Array dinamici multidimensionali
79
Array dinamici multidim.
¤ È possibile creare un array dinamico multidimensionale
attraverso l’espressione new[]
¤ L’espressione new[] restituisce l’indirizzo del primo
elemento dell’array multidim.
¤ Gli elementi di un un array multidim. sono a loro volta array
¤ Un puntatore al primo elemento è un puntatore ad array
int (*matrix)[3] = new int[3][3];
80
Array dinamici multidim.
Heap
Stack
matrix: int(*)[3]
int[3][3
]
!
81
Alias per i sotto-array
¤ È possibile aumentare la leggibilità del codice definendo
un alias per il tipo “riga della matrice”
¤ La scrittura evidenzia come la new[] restituisca un
puntatore alla prima riga della matrice
using matrix_row = int[3];
matrix_row* matrix = new int[3][3];
82
Nascondere il tipo dei sotto-array
¤ Dal C++11, è possibile far sì che sia il compilatore a
dedurre il tipo dato di una variabile definendo la
variabile come auto
¤ Tale funzionalità può essere usata per aumentare la
leggibilità
auto matrix = new int[3][3];
83
Inizializzare array dinamici
multidimensionali
¤ Dal C++11, è possibile inizializzare un array dinamico
multidim. in maniera simile ad un normale array multidim.
auto matrix = new int[3][3]{{1, 7, 14},
{8, 16, 12},
{27, 32, 5}};
84
Distruggere array dinamici
multidimensionali
¤ Così come per i normali array dinamici, l’espressione
delete[] consente di distruggere array
multidimensionali
delete[] matrix;
85
Array dinamici multidim.
di dimensione variabile
¤ Tutte le dimensioni dell’array, con la sola eccezione della
prima, devono essere note a tempo di compilazione
¤ matrix è un array dinamico di N elementi di tipo int[3]
¤ Essendo dinamico, N può essere noto a run time
¤ Il tipo dell’elemento (int[3]) deve però essere noto a
compile time
size_t N = 3;
auto matrix = new int[N][3] // OK
auto matrix2 = new int[3][N] // ERROR!
86
Bibliografia
87
Bibliografia
¤ S. B. Lippman, J. Lajoie, B. E. Moo, C++ Primer (5th Ed.)
¤ B. Stroustrup, The C++ Programming Language (4th Ed.)
¤ S. Meyers, Effective C++ (2nd Ed.)
¤ R. Lafore, Object Oriented Programming in C++ (4th Ed.)
88
Bibliografia
¤ C++ Reference, Zero initialization
http://en.cppreference.com/w/cpp/language/zero_initializ
ation
¤ C++ Reference, Value initialization
http://en.cppreference.com/w/cpp/language/value_initiali
zation
¤ C++ Reference, new
expressionhttp://en.cppreference.com/w/cpp/language/n
ew
¤ C++ Reference, delete expression
http://en.cppreference.com/w/cpp/language/delete
89
Bibliografia
¤ Wikipedia, Data Segment
http://en.wikipedia.org/wiki/Data_segment
¤ University of Alberta, Understanding memory
http://www.ualberta.ca/CNS/RESEARCH/LinuxClusters/m
em.html
¤ G. Duarte, Anatomy of a program in memory
http://duartes.org/gustavo/blog/post/anatomy-of-a-
program-in-memory
90
Bibliografia
¤ Stackoverflow FAQ, “How do I use arrays in C++?”
http://stackoverflow.com/questions/4810664/how-do-i-
use-arrays-in-c
¤ Stackoverflow FAQ, “What are Aggregates and PODs
and how/why are they special?”
http://stackoverflow.com/q/4178175/1849221
91

Gestione della memoria in C++

  • 1.
    Gestione della memoria IlioCatallo – info@iliocatallo.it
  • 2.
    Outline ¤ Tempo divita degli oggetti ¤ Segmentazione della memoria ¤ Oggetti dinamici ¤ Strutture dinamiche ¤ Array dinamici ¤ Array monodimensionali ¤ Array multidimensionali 2
  • 3.
    Tempo di vitadegli oggetti 3
  • 4.
    Oggetti ¤ Una variabilepuò dunque essere definita come un oggetto con nome ¤ RICORDA: il termine oggetto può anche essere usato come sinonimo di istanza di una classe ¤ Le due definizioni non sono equivalenti Un oggetto è una regione di memoria di un determinato tipo dato 4
  • 5.
    Oggetti ¤ Esempio: oggettosenza nome (temporaneo) di tipo int ¤ Esempio: oggetto con nome (variabile) di tipo int std::cout << (5+3)*3 << std::endl; int result = (5+3)*3; // ‘result’ is the name of // an object of type int std::cout << result << std::endl; 5
  • 6.
    Tempo di vitadi un oggetto ¤ Ogni oggetto in un programma è caratterizzato da un proprio tempo di vita (storage duration) ¤ Tempo di vita: l’intervallo di tempo che intercorre tra la creazione dell’oggetto e la sua distruzione 6
  • 7.
    Tempo di vitadi un oggetto ¤ Perchè limitare il tempo di vita degli oggetti? ¤ Per preservare spazio in memoria ¤ Esempio: se una funzione non è in esecuzione, non c’è motivo di mantenere in memoria le corrispettive variabili locali 7
  • 8.
    Tempo di vitadi un oggetto ¤ A seconda del suo tempo di vita, un oggetto può essere classificato come: ¤ Temporaneo ¤ Automatico ¤ Statico ¤ Dinamico ¤ Thread-local 8
  • 9.
    Oggetti temporanei ¤ Glioggetti temporanei contengono risultati intermedi ottenuti durante la valutazione di un’epressione ¤ Esempio: Il risultato di y*z deve essere salvato da qualche parte prima di poter essere aggiunto a x int v = x + y*z; 9
  • 10.
    Oggetti temporanei ¤ L’oggettotemporaneo non ha un nome ¤ La loro creazione è invisibile all’utente ¤ Non è possibile assegnargli un valore ¤ L’oggetto viene distrutto al termine della full-expression che lo contiene ¤ Full-expression: un’espressione che non è contenuta in un’espressione più grande int v = x + y*z; 10
  • 11.
    Oggetti automatici ¤ Unoggetto automatico viene creato al momento della sua definizione e distrutto al termine del blocco che lo contiene ¤ Esempio: la variabile locale x viene distrutta al termine del blocco, ed è quindi un oggetto automatico void do_nothing() { int x = 12; } 11
  • 12.
    Oggetti automatici ¤ Attenzionea non confendere i due concetti: ¤ Il nome della variabile è locale: il nome è visibile unicamente all’interno del blocco ¤ L’oggetto è automatico: la variabile verrà distrutta al termine del blocco una variabile locale è un oggetto automatico 12
  • 13.
    Oggetti statici ¤ Unoggetto statico viene creato ad avvio del programma e distrutto al termine del programma ¤ Esempi di oggetti statici: ¤ Variabili globali ¤ Variabili locali dichiarate come static 13
  • 14.
    Oggetti statici ¤ Unavariabile globale è una variabile il cui nome è visibile in ogni punto del programma ¤ La variabile globale x è un oggetto statico, la sua vita termina al termine del programma int x = 10; int main() { x = 5; // 'x' is a global variable std::cout << x << std::endl; } 14
  • 15.
    Oggetti statici ¤ Unavariabile locale static è inizializzata a zero ad avvio del programma, e distrutta al termine dell’applicativo ¤ Esempio: la variabile ctr viene incrementata ad ogni chiamata di count_calls() size_t count_calls() { static size_t ctr; return ++ctr; } 15
  • 16.
    Oggetti statici ¤ ilblocco che contiene una variabile locale static determina la visibilità della variabile, ma non il suo tempo di vita ¤ Il nome della variabile è visibile solo all’interno della funzione, ma… ¤ La variabile viene distrutta al termine del programma, non al termine della funzione size_t count_calls() { static size_t ctr; return ++ctr; } 16
  • 17.
    Oggetti dinamici ¤ Unavolta creato, un oggetto dinamico rimane in memoria fino a quando non viene esplicitamente richiesta la sua distruzione ¤ Il tempo di vita di un oggetto dinamico è gestito dal programmatore, che decide: ¤ Quando creare l’oggetto dinamico ¤ Quando distruggere l’oggetto 17
  • 18.
    Oggetti dinamici int Il programmatore creaun oggetto dinamico di tipo int 5 int L’oggetto dinamico viene utilizzato 5 int Il programmatore distrugge l’oggetto quando non più necessario 18
  • 19.
  • 20.
    Processo in memoria ¤Il sistema operativo (SO) assegna ad ogni programma in esecuzione (processo) una porzione della memoria disponibile ¤ Tale memoria permette l’effettiva esecuzione del programma ¤ Esempio: una parte viene adoperata per mantenere in memoria il valore delle variabili 20
  • 21.
    Processo in memoria ¤La porzione di memoria dedicata al processo viene chiamata lo spazio d’indirizzamento (address space) del processo memoria assegnata al programma in esecuzione 1775 3472… 21
  • 22.
    Segmentazione ¤ Lo spaziod’indirizzamento di un processo è suddiviso in quattro aree (segmenti): ¤ Code segment* ¤ Data segment ¤ Heap segment ¤ Stack segment ¤ Ogni segmento viene utilizzato per uno scopo diverso *anche detto text segment 22
  • 23.
    Segmentazione ¤ La classicarappresentazione dello spazio d’indirizzamento è ottenuta semplicemente ruotando la figura di 90° memoria assegnata al programma in esecuzione Stack 1775 3472… HeapDataCode 23
  • 24.
  • 25.
    Code segment ¤ Ilcode segment è un’area di memoria a sola lettura che contiene il codice che verrà eseguito Stack Heap Data movl $12, -12(%rbp) movl $13, -16(%rbp) movl -12(%rbp), %eax int main() { int a = 12, b = 13; int c = a + b; } 25
  • 26.
    Data segment ¤ Ildata segment contiene variabili globali e variabili locali static Stack Heap x: 10 ctr: 0 Code int x = 10; size_t count_calls() { static size_t ctr; return ++ctr; } int main() { count_calls(); return 0; } 26
  • 27.
    Stack segment ¤ Lostack mantiene in memoria le variabili locali ed i parametri in ingresso alle funzioni int main() { int a = 15, b = 10; return 0; } a: 15 b: 10 spazio rimanente nello stack Heap Data Code 27
  • 28.
    Stack segment ¤ Lostack è organizzato come una pila, ogni volta che una funzione viene invocata, viene aggiunto un nuovo strato ¤ Il nuovo strato (frame) contiene le variabili locali ed i parametri in ingresso della funzione appena invocata* ¤ Quando una funzione termina, il corrispettivo frame viene rimosso dalla pila ¤ Le variabili locali vengono automaticamente distrutte * il frame contiene anche informazioni che permettono, una volta termina una funzione, che il controllo ritorni alla funzione chiamante 28
  • 29.
    Stack segment void do_nothing2(){ int y = 13; } void do_nothing1() { int x = 12; do_nothing2(); } int main() { int a = 15, b = 10; do_nothing1(); return 0; } a: 15 b: 10 x: 12 y:13 spazio rimanente nello stack main() do_nothing1() do_nothing2() Frame 1 Frame 2 Frame 3 29
  • 30.
    Heap segment ¤ L’heapsegment è l’area di memoria che contiene gli oggetti dinamici ¤ A differenza dello stack, un nuovo oggetto può occupare una posizione casuale all’interno dell’heap ¤ Conseguenza: non esiste nessun meccanismo che automaticamente elimini gli oggetti non più utilizzati Heap Occupato Libero 30
  • 31.
  • 32.
    Creare oggetti dinamici ¤Siamo al corrente di un solo modo per creare un oggetto che sia successivamente modificabile: ¤ Assegnargli un nome, in altre parole definire una variabile Come possiamo creare un oggetto dinamico? 32
  • 33.
    Creare oggetti dinamici ¤Assegnare un nome ad un oggetto comporta però la definizione del suo tempo di vita ¤ Variabile locale? oggetto automatico ¤ Variabile locale static? oggetto statico ¤ variabile globale? oggetto statico Come possiamo creare un oggetto dinamico? 33
  • 34.
    Creare oggetti dinamici ¤Un oggetto dinamico deve quindi essere anonimo, in altre parole, non deve avere un nome Come possiamo manipolare un oggetto senza nome? 34
  • 35.
    Creare oggetti dinamici ¤Un oggetto dinamico deve quindi essere anonimo, in altre parole, non deve avere un nome ¤ Anche se senza nome, l’oggetto dinamico deve avere un indirizzo ¤ IDEA: usare un puntatore per contenere l’indirizzo dell’oggetto dinamico Come possiamo manipolare un oggetto senza nome? 35
  • 36.
    Espressione new ¤ L’espressionenew permette di creare oggetti dinamici ¤ Notare come: ¤ a sia una variabile locale, dunque un oggetto automatico ¤ l’oggetto int anonimo sia invece un oggetto dinamico int* a = new int; // 'a' is pointing to an unnamed, // dynamic object of type int 36
  • 37.
    Espressione new ¤ L’espressionenew permette di creare oggetti dinamici ¤ Ricorda: la variabile a non è l’oggetto dinamico, è un puntatore all’oggetto dinamico int* a = new int; // 'a' is pointing to an unnamed, // dynamic object of type int 37
  • 38.
    Rappresentazione in memoria Heap Stack a:int* int Un oggetto dinamico non ha nome 38
  • 39.
    Espressione new ¤ L’espressionenew esegue due operazioni: ¤ Invoca l’operatore new, che alloca nell’heap il quantitativo di memoria necessaria ¤ Costruisce l’oggetto nell’area di memoria designata ¤ L’espressione new restituisce l’indirizzo dell’oggetto dinamico int* a = new int; 39
  • 40.
    Inizializzare oggetti dinamici ¤Gli oggetti dinamici sono inizializzati a default ¤ I tipi built-in contengono quindi un valore non noto ¤ Istanze di una classe vengono inizializzati attraverso il costruttore di default ¤ Come per gli oggetti automatici, è quindi buona norma inizializzare gli oggetti dinamici con valori sensati 40
  • 41.
    Inizializzare oggetti dinamici ¤Sono possibili diversi tipi di inizializzazione: ¤ Inizializzazione diretta: l’oggetto puntato da d vale 12 ¤ Inizializzazione per copia: l’oggetto puntato da c vale 20 ¤ Inizializzazione per valore: l’oggetto puntato da e vale 0 int b = 20; int* d = new int(12); // direct initialization int* c = new int(b); // copy initialization int* e = new int(); // value initialization 41
  • 42.
    Modificare oggetti dinamici ¤Per alterare oggetto dinamici si utilizza l’operatore di indirezione * int* a = new int; int b = 12; *a = b + 7; std::cout << "the value pointed to by a is ” << *a << std::endl; } 42
  • 43.
    Distruggere oggetti dinamici ¤Gli oggetti dinamici vanno esplicitamente distrutti ¤ Ogni oggetto allocato occupa parte della memoria disponibile nell’heap ¤ La memoria non è illimitata ¤ È tecnicamente possibile saturare tutto lo spazio a disposizione 43
  • 44.
    Espressione delete ¤ L’espressionedelete permette di distruggere un oggetto dinamico ¤ Dopo la delete, l’oggetto dinamico puntato da a non esiste più int* a = new int; int b = 12; *a = b + 7; delete a; 44
  • 45.
  • 46.
    Espressione delete ¤ L’espressionedelete esegue due operazioni: ¤ Invoca l’operatore delete, così da distrugge l’oggetto ¤ Dealloca la memoria precedentemente allocata, rendendola così nuovamente disponibile delete a; 46
  • 47.
    Dangling pointers ¤ L’espressionedelete distrugge l’oggetto dinamico ¤ Il corrispettivo puntatore non è quindi più valido ¤ Infatti, l’oggetto puntato non esiste più ¤ Il puntatore continua però a contenere l’indirizzo di dove una volta si trovava l’oggetto dinamico 47
  • 48.
    Dangling pointers ¤ L’espressionedelete distrugge l’oggetto dinamico ¤ Il puntatore è diventato un dangling pointer ¤ Dangling pointer: puntatore che punta ad un area di memoria precedentemente occupata da un oggetto non più esistente 48
  • 49.
    Dangling pointers ¤ Lasoluzione è quindi rafforzare il fatto che il puntatore non sta più puntando a nulla ¤ Regola: dopo la distruzione di un oggetto dinamico, il corrispettivo puntatore deve essere posto a nullptr (o NULL) delete a; a = nullptr; 49
  • 50.
  • 51.
    Strutture ¤ Le strutturegeneralizzano il concetto di array: ¤ Un array è una sequenza di elementi dello stesso tipo ¤ Una struttura è un sequenza di elementi di tipo diversi 51 Una struttura è una sequenza di elementi (membri) di tipo arbitrario
  • 52.
    Strutture ¤ Address èun tipo composto e definito dall’utente ¤ Una volta che il tipo Address è stato definito, è possibile dichiarare variabili di tipo Address: 52 struct Address { std::string street; int number; std::string town; }; Address polimi_address;
  • 53.
    Inizializzare una struttura ¤Dal C++11, è possibile inizializzare oggetti di tipo struttura mediante una lista di inizializzazione* 53 * prima del C++11, ciò era possibile unicamente nel caso in cui la struttura fosse stata anche un aggregato Address polimi_address = {"Piazza Leonardo Da Vinci", 32, "Milan"};
  • 54.
    Member access operator ¤I membri di una struttura sono accessibili mediante l’operatore . (member access operator) 54 polimi_address.street = "Piazza Leonardo Da Vinci"; polimi_address.number = 32; polimi_address.town = "Milan”;
  • 55.
    Strutture dinamiche ¤ Èpossibile dichiarare strutture dinamiche mediante l’espressione new ¤ L’espressione new restituisce l’indirizzo della struttura dinamica allocata nell’heap 55
  • 56.
    Strutture dinamiche ¤ Esempio:la struttura polico_address è inizializzata a default 56 Address* polico_address = new Address; // default // initialization
  • 57.
    Rappresentazione in memoria 57 Heap Stack polico_address:Address* Un struttura dinamica non ha nome street: number: town: Address
  • 58.
    Inizializzare una strutturadinamica ¤ Dal C++11, una struttura dinamica può essere inizializzata con una lista di inizializzazione* 58 Address* polico_address = new Address{“Via Valleggio", 11, ”Como"};// uniform // initialization
  • 59.
    Accedere a membridi una struttura dinamica ¤ Per accedere alla struttura dinamica è necessario: ¤ Accedere alla struttura mediante l’operatore * ¤ Accedere a un particolare membro mediante l’operatore . 59 (*polico_address).street = "Via Valleggio”; Mediante indirezione recuperiamo la struttura Mediante l’operatore di accesso alteriamo un particolare membro della struttura
  • 60.
    Accedere a membridi una struttura dinamica ¤ Per accessi a membri di una struttura mediante puntatore è disponibile una variante del member access operator ¤ L’operatore -> è talvolta indicato come pointer-to-member operator 60 polico_address->street = "Via Valleggio”;
  • 61.
    Distruggere una strutturadinamica ¤ Una struttura dinamica si distrugge mediante l’utilizzo della espressione delete 61 delete polico_address;
  • 62.
  • 63.
    Creare array dinamici ¤Un array dinamico può essere creato mediante l’espressione new[] ¤ L’espressione new restituisce l’indirizzo del primo elemento dell’array (non avviene alcun decadimento) ¤ Nell’esempio, tale indirizzo viene salvato in un puntatore di nome numbers int* numbers = new int[5]; // dynamic (hence, unnamed) array // of 5 elements of type int 63
  • 64.
    Creare array dinamici Heap Stack numbers:int* int[5] Un array dinamico non ha nome (array anonimo) 64
  • 65.
    Inizializzare un arraydinamico ¤ Dall C++11, un array dinamico può essere inizializzato in in modo simile ad un comune array*: ¤ Tale modalità di inizializzazione si chiama inizializzazione uniforme (uniform initialization) int* numbers = new int[5]{1, 7, 13, 5, 9}; 65 *Differentemente dai comuni array è però sempre necessario specificare la dimensione
  • 66.
    Accedere ad unarray dinamico ¤ L’accesso ad un array dinamico avviene secondo le stesse modalità di un comune array ¤ Infatti, come sappiamo l’operatore [] è un operatore definito sui puntatori int* numbers = new int[5]{1, 7, 13, 5, 9}; numbers[2] = 20; // implemented as *(numbers + 2) = 20 66
  • 67.
    Distruggere un arraydinamico ¤ Il programmatore è responsabile della distruzione di array dinamici non più necessari ¤ L’espressione delete[] distrugge un array dinamico delete[] numbers; 67
  • 68.
    Distruggere un arraydinamico ¤ L’uso delle [] è cruciale ¤ È l’unico modo per chiedere al compilatore di distruggere un array di oggetti, piuttosto che un singolo oggetto ¤ Se non usato, si incappa in un undefined behavior delete[] numbers; 68
  • 69.
  • 70.
    Dimensione di unarray ¤ La dimensione di un array automatico deve essere nota a tempo di compilazione (compile time) ¤ Tale imposizione facilita la gestione della stack segment constexpr size_t DIM = 5; int numbers[DIM]; 70
  • 71.
    Dimensione di unarray vs stack ¤ Invocare una funzione significa impilare un nuovo frame nello stack segment void do_nothing() { int x = 2; char y[2] ={5, 4}; int c = 3; } int main() { int a = 12; do_nothing(); return 0; } a: 12 x: 2 y[0]: 5 y[1]: 4 c: 3 spazio rimamente nello stack main() do_nothing() 71
  • 72.
    Frame pointer estack pointer ¤ Durante l’esecuzione di una funzione, possiamo identificare due indirizzi importanti in memoria ¤ Stack pointer: l’indirizzo dell’ultima cella di memoria valida nello stack ¤ Frame pointer: l’indirizzo della prima cella di memoria appartenente al frame attualmente in esecuzione 72
  • 73.
    Frame pointer estack pointer ¤ Se supponiamo che do_nothing() sia in esecuzione, si ha che: a: 12 x: 2 y[0]: 5 y[1]: 4 c: 3 spazio rimamente nello stack do_nothing() stack pointer frame pointer main() 73
  • 74.
    Frame pointer estack pointer ¤ Gli indirizzi delle variabili locali della funzione in esecuzione sono deducibili a partire dall’indirizzo contenuto nel frame pointer ¤ Dove lo spiazzamento è misurato in byte indirizzo var. locale = frame pointer + spiazzamento 74
  • 75.
    Frame pointer estack pointer ¤ Se supponiamo che un int occupi 2 byte, e un char 1 byte a: 12 x: 2 y[0]: 5 y[1]: 4 c: 3 spazio rimamente nello stack stack pointer frame pointer frame pointer + 0 frame pointer + 2 byte frame pointer + 3 byte frame pointer + 4 byte 75
  • 76.
    Dimensione di unarray ¤ Se richiediamo che le variabili abbiano dimensioni note a priori tutti gli spiazzamenti sono calcolabili a compile-time ¤ Conseguenza: è possibile conoscere a tempo di compilazione la dimensione del frame di una funzione ¤ Questo comporta: ¤ Codice più veloce ¤ Maggiore semplicità nella scrittura dei compilatori 76
  • 77.
    Array di dimensionivariabili ¤ Non possiamo usare normali array, perchè verrebbero allocati nello stack, totalmente gestito dal compilatore ¤ Al contrario, l’heap è una porzione di memoria totalmente a disposizione del programmatore ¤ IDEA: memorizzare l’array di dimensioni variabili all’interno dell’heap segment Come definire un array la cui dimensione venga specificata a tempo d’esecuzione (run time)? 77
  • 78.
    Array di dimensionivariabili ¤ Al momento della creazione di un array dinamico, la dimensione può essere una qualsiasi espressione ¤ Non è richiesto che l’espressione sia costante ¤ Nell’esempio, dim non è una quantità costante, ma può comunque essere usata per creare un array dinamico size_t dim = 5; int* numbers = new int[dim]; 78
  • 79.
  • 80.
    Array dinamici multidim. ¤È possibile creare un array dinamico multidimensionale attraverso l’espressione new[] ¤ L’espressione new[] restituisce l’indirizzo del primo elemento dell’array multidim. ¤ Gli elementi di un un array multidim. sono a loro volta array ¤ Un puntatore al primo elemento è un puntatore ad array int (*matrix)[3] = new int[3][3]; 80
  • 81.
  • 82.
    Alias per isotto-array ¤ È possibile aumentare la leggibilità del codice definendo un alias per il tipo “riga della matrice” ¤ La scrittura evidenzia come la new[] restituisca un puntatore alla prima riga della matrice using matrix_row = int[3]; matrix_row* matrix = new int[3][3]; 82
  • 83.
    Nascondere il tipodei sotto-array ¤ Dal C++11, è possibile far sì che sia il compilatore a dedurre il tipo dato di una variabile definendo la variabile come auto ¤ Tale funzionalità può essere usata per aumentare la leggibilità auto matrix = new int[3][3]; 83
  • 84.
    Inizializzare array dinamici multidimensionali ¤Dal C++11, è possibile inizializzare un array dinamico multidim. in maniera simile ad un normale array multidim. auto matrix = new int[3][3]{{1, 7, 14}, {8, 16, 12}, {27, 32, 5}}; 84
  • 85.
    Distruggere array dinamici multidimensionali ¤Così come per i normali array dinamici, l’espressione delete[] consente di distruggere array multidimensionali delete[] matrix; 85
  • 86.
    Array dinamici multidim. didimensione variabile ¤ Tutte le dimensioni dell’array, con la sola eccezione della prima, devono essere note a tempo di compilazione ¤ matrix è un array dinamico di N elementi di tipo int[3] ¤ Essendo dinamico, N può essere noto a run time ¤ Il tipo dell’elemento (int[3]) deve però essere noto a compile time size_t N = 3; auto matrix = new int[N][3] // OK auto matrix2 = new int[3][N] // ERROR! 86
  • 87.
  • 88.
    Bibliografia ¤ S. B.Lippman, J. Lajoie, B. E. Moo, C++ Primer (5th Ed.) ¤ B. Stroustrup, The C++ Programming Language (4th Ed.) ¤ S. Meyers, Effective C++ (2nd Ed.) ¤ R. Lafore, Object Oriented Programming in C++ (4th Ed.) 88
  • 89.
    Bibliografia ¤ C++ Reference,Zero initialization http://en.cppreference.com/w/cpp/language/zero_initializ ation ¤ C++ Reference, Value initialization http://en.cppreference.com/w/cpp/language/value_initiali zation ¤ C++ Reference, new expressionhttp://en.cppreference.com/w/cpp/language/n ew ¤ C++ Reference, delete expression http://en.cppreference.com/w/cpp/language/delete 89
  • 90.
    Bibliografia ¤ Wikipedia, DataSegment http://en.wikipedia.org/wiki/Data_segment ¤ University of Alberta, Understanding memory http://www.ualberta.ca/CNS/RESEARCH/LinuxClusters/m em.html ¤ G. Duarte, Anatomy of a program in memory http://duartes.org/gustavo/blog/post/anatomy-of-a- program-in-memory 90
  • 91.
    Bibliografia ¤ Stackoverflow FAQ,“How do I use arrays in C++?” http://stackoverflow.com/questions/4810664/how-do-i- use-arrays-in-c ¤ Stackoverflow FAQ, “What are Aggregates and PODs and how/why are they special?” http://stackoverflow.com/q/4178175/1849221 91