Newsgroup: it.comp.lang.c++
Titolo: FAQ 10 -- RISPOSTE

Che cosa e` la memoria dinamica? Come si usa?
Lascio a voi la risposta. Fate degli esempi brevi ma significativi
evidenziando le differenze tra malloc, new, new[] ed i rispettivi duali.
Paolo
-----------------------------------------------
  Paolo Marotta
  Computer Science, University of Pisa
  home:   
http://www.cli.di.unipi.it/~marotta
-----------------------------------------------

 evidenziando le differenze tra malloc, new, new[] ed i rispettivi duali.
La memoria, come qualsiasi altra risorsa, puo` essere
allocata "dinamicamente", cioe` al momento del bisogno.
Rispetto all'alternativa di allocare risorse "staticamente"
(ovvero "prima di iniziare", "una volta per tutte"),
questo dinamismo presenta vantaggi e svantaggi.  In
pratica, la flessibilita` dell'allocazione dinamica e` quasi
irrinunciabile nei programmi moderni.
L'allocazione dinamica di un oggetto di tipo T si puo`
ottenere, in C++, usando la forma:
    T* pT = new T;    // per usare il ctor di default
oppure:
    T* pT = new T(argomenti);    // per usare altri ctor
Quando si usa questa forma, occorre prima o poi che
il programma esegua anche l'operazione duale
    delete pT;
e _garantire_ questo puo` essere scomodo; meglio,
quando possibile, usare il costrutto std::auto_ptr<>:
    std::auto_ptr<T> pT = new T;
pT si comporta come un T*, ma il suo distruttore
provvedera` automaticamente a fare il delete "per
nostro conto".
Se di oggetti di tipo T ne puo` servire "un certo numero",
invece di "uno", si usa invece, di solito, std::vector<>:
    std::vector<T> vT;
    // un T alla volta...:
    vT.push_back(T());    // con il ctor di default
    // e/o, anche, molti T alla volta...:
    vT.resize(23);           // altri 22 ctor di default
    vT.push_back(T(args));    // con altri ctor
Questo ha tra l'altro gli stessi vantaggi di std::auto_ptr<>,
in quanto gli oggetti contenuti in vT vengono automaticamente
cancellati dal distruttore di vT stesso.
Esistono anche altri contenitori standard, come std::list<>,
std::set<>, std::map<>, ..., per esigenze particolari, ma
std::vector<> e` di gran lunga il piu` utile e comune per
la semplice allocazione dinamica.  Per il caso particolare,
molto comune, di stringhe di caratteri di lunghezza
dinamicamente variabile, std::string (e std::wstring,
per usare Unicode) sono anche molto, molto pratiche.
Il C++, essendo un linguaggio adatto anche a "programmazione
di sistema" (cioe` di bassissimo livello), offre anche delle
forme alternative di allocazione dinamica, che sono quelle
che, internamente, strutture come std::vector possono
usare per offrire la loro comoda ed efficiente funzionalita`
(queste funzionalita` vanno incapsulate in un oggetto di
tipo "allocatore" perche` std::vector e soci possano poi
usarle).  In pratica, non si presenta mai una vera necessita`
di accedere a queste funzionalita` di bassissimo livello
(come new[]/delete[], malloc/free, e altre, meno portatili,
come sbrk su Unix, VirtualAllocEx su Win32, CoTaskMemAlloc
nell'ambito di un utilizzo del COM o DCOM, eccetera eccetera),
se non per compatibilita` con vecchio codice scritto con
l'uso di esse, o interfacce di sistema di bassissimo livello;
infatti, std::vector (e gli altri contenitori standard) fanno
in pratica tutto quello che serve, al limite con l'aiuto di un
oggetto allocatore non-standard per esigenze peculiari.
La stragrande maggioranza dei programmatori C++ non ha
alcun bisogno di approfondire tutti questi altri approcci, se
non sulla base del "se lo conosci, lo eviti":-).
Alex

 Che cosa e` la memoria dinamica? Come si usa?
La memoria dinamica è memoria che viene richiesta esplicitamente dal
programmatore attraverso l'operatore std::new. Una volta utilizzata deve
essere rilasciata utilizzando l'operatore std::delete. E' necessario
specificare il tipo di dato per il quale si vuole allocare memoria. Tale
memoria viene normalmente gestita attraverso puntatori. Es. (T è un tipo
qualunque, anche definito dall'utente come struct o class):
T *p = std::new T;
alloca memoria per un elemento di tipo T. Alla fine scriverò
std::delete p;
per rilasciarla. Per accedere a tale memoria utilizzerò l'operatore di
dereferenza:
int *p = std::new T;
*p = 123;
std::cout << *p << std:endl;
std::delete p;
Se volessi allocare non un solo elemento di tipo T ma un array dovrei
utilizzare una forma leggermente diversa dei due operatori di cui sopra:
T *p = std::new T [ nn ];
dove nn è il numero di elementi che voglio allocare. Per rilasciare
questa memoria devo scrivere
std::delete [] p;
L'operatore std::new, in caso non ci sia abbastanza memoria libera per
soddisfare la richiesta, lancia un'eccezione di tipo std::bad_alloc che
è consigliabile intercettare.
La libreria standard del C (cstdlib) offre un metodo alternativo basato
sulle funzioni malloc() e free(). Es.:
T *p = ( T* ) malloc( sizeof T );
if ( p == 0 )
{
    // Gestire l'errore
}
...
free( p );
EMG


Ma std::new e std::delete sono diversi dalle keyword new e delete?
--
== Aldo Brucale ===================

 Ma std::new e std::delete sono diversi dalle keyword new e delete?
Con l'introduzione nel C++ dei namespaces dovrebbe essere diventato
obbligatorio indicare quello di appartenenza di ogni simbolo utilizzato
all'interno dei programmi. In questo caso, omettendolo farei riferimento
agli operatori new e delete del namespace corrente che, in mancanza di
altre indicazioni, dovrebbe essere quello globale. Mentre invece i due
operatori che mi interessano fanno parte di quello chiamato std.
Alternative sono di utilizzare
using namespace std;
o
using std::new;
using std::delete;
all'inizio del programma.
EMG

 operatori che mi interessano fanno parte di quello chiamato std.
Hmmm, sei sicuro...?  Non vedo come possa esistere uno std::new, visto
che new e` una keyword, esattamente come non puo` esistere std::if o
std::while.
"operator new" e` un'altra cosa, ma, naturalmente, si scrive, appunto,
"operator new", e non puoi omettere la keyword "operator" davanti alla
keyword "new" (cosi` come puoi scrivere "operator +", ma non "std::+").
Non mi pare tu possa ridefinire "operator new" a livello di namespace
(solo a livello di classe, che non e` proprio la stessa cosa).  In particolare,
non mi pare che "operator new" sia definito nel namespace std.  E se lo
fosse, ancora non mi pare sarebbe comunque giusto usare std::new
(se mai, std::operator new... penso!).
Non sono sicuro al 100%, da cui i "non vedo" e "non mi pare" dei
paragrafi precedenti...
Alex

 paragrafi precedenti...
Mi interesserebbe averne la conferma.
EMG