Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Nella programmazione C++ moderna, la libreria standard include puntatori intelligenti, che vengono usati per garantire che i programmi siano privi di memoria e perdite di risorse e siano sicuri dalle eccezioni.
Utilizzi per i puntatori intelligenti
I puntatori intelligenti sono definiti nel namespace std nel file header <memory>. Sono cruciali per l'idioma di programmazione Resource Acquisition Is Initialization(RAII). L'obiettivo principale di questo linguaggio è garantire che l'acquisizione delle risorse si verifichi contemporaneamente all'inizializzazione dell'oggetto. Tutte le risorse per l'oggetto vengono create e pronte in una riga di codice.
In pratica, il principio principale di RAII consiste nell'assegnare la proprietà di qualsiasi risorsa allocata all'heap a un oggetto allocato dallo stack il cui distruttore contiene il codice per eliminare o liberare la risorsa e anche qualsiasi codice di pulizia associato. Tali oggetti includono memoria allocata dinamicamente o handle di oggetti di sistema.
Nella maggior parte dei casi, quando si inizializza un puntatore raw o un handle di risorsa in modo che faccia riferimento a una risorsa effettiva, passare immediatamente il puntatore a uno smart pointer. Nel C++ moderno, i puntatori grezzi vengono usati solo in piccoli blocchi di codice con ambito limitato, loop o funzioni di supporto in cui le prestazioni sono critiche e non vi è alcuna possibilità di confusione riguardo alla proprietà.
Nell'esempio seguente viene fatto un confronto tra una dichiarazione di puntatore non elaborato e una dichiarazione di puntatore intelligente.
void UseRawPointer()
{
// Using a raw pointer -- not recommended.
Song* pSong = new Song(L"Nothing on You", L"Bruno Mars");
// Use pSong...
// Don't forget to delete!
delete pSong;
}
void UseSmartPointer()
{
// Declare a smart pointer on stack and pass it the raw pointer.
unique_ptr<Song> song2(new Song(L"Nothing on You", L"Bruno Mars"));
// Use song2...
wstring s = song2->duration_;
//...
} // song2 is deleted automatically here.
Come illustrato nell'esempio, un puntatore intelligente è un modello di classe dichiarato nello stack e inizializzato tramite un puntatore non elaborato che punta a un oggetto allocato dall'heap. Dopo essere stato inizializzato, il puntatore intelligente assume la proprietà del puntatore grezzo. Questo approccio significa che il puntatore intelligente è responsabile dell'eliminazione della memoria specificata dal puntatore non elaborato.
Il distruttore del puntatore intelligente contiene la chiamata a delete. Poiché il puntatore intelligente viene dichiarato sullo stack, il suo distruttore viene invocato quando il puntatore intelligente non è più nell'ambito di validità. Viene richiamato anche se viene generata un'eccezione in un punto più alto dello stack.
Accedere al puntatore incapsulato usando gli operatori del puntatore familiari: -> e *. La classe smart pointer sovraccarica questi operatori per restituire il puntatore raw incapsulato.
Il linguaggio del puntatore intelligente C++ è simile alla creazione di oggetti in linguaggi come C#. Si crea l'oggetto e quindi si lascia che il sistema si occupi dell'eliminazione al momento corretto. La differenza è che nessun Garbage Collector separato viene eseguito in background. La memoria viene gestita tramite le regole di ambito C++ standard in modo che l'ambiente di runtime sia più veloce ed efficiente.
Importante
Creare sempre puntatori intelligenti su una riga di codice separata, mai in un elenco di parametri. Questo approccio impedisce una perdita di risorse sottile a causa di determinate regole di allocazione dell'elenco di parametri.
Nell'esempio seguente viene illustrato come usare un unique_ptr tipo di puntatore intelligente dalla libreria standard C++ per incapsulare un puntatore a un oggetto di grandi dimensioni.
class LargeObject
{
public:
void DoSomething(){}
};
void ProcessLargeObject(const LargeObject& lo){}
void SmartPointerDemo()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Pass a reference to a method.
ProcessLargeObject(*pLarge);
} //pLarge is deleted automatically when function block goes out of scope.
Di seguito vengono illustrati i passaggi essenziali per utilizzare i puntatori intelligenti.
Dichiarare il puntatore intelligente come variabile automatica (locale). Non usare l'espressione
newomallocsul puntatore intelligente stesso.Nel parametro di tipo specificare il tipo di riferimento del puntatore incapsulato.
Passare un puntatore grezzo a un oggetto
newnel costruttore dello smart pointer. Alcune funzioni di utilità o costruttori di puntatori intelligenti eseguono automaticamente questa operazione.Utilizzare gli operatori
->e*sovraccaricati per accedere all'oggetto.Consentire al puntatore intelligente di eliminare l'oggetto.
I puntatori intelligenti sono progettati per essere estremamente efficaci sia in termini di memoria che di prestazioni. Ad esempio, l'unico membro dati in unique_ptr è il puntatore incapsulato. Questo significa che unique_ptr corrisponde esattamente alla stessa dimensione del puntatore, ovvero quattro byte o otto byte. Accedere al puntatore incapsulato usando gli operatori sovraccaricati * e -> dello smart pointer non è significativamente più lento dell'accesso diretto ai puntatori grezzi.
I puntatori intelligenti dispongono di funzioni membro proprie, alle quali si accede usando la notazione con il punto. Ad esempio, alcuni puntatori intelligenti della libreria standard C++ hanno una reset funzione membro che rilascia la proprietà del puntatore. Questo comportamento è utile quando si desidera liberare la memoria posseduta dal puntatore intelligente prima che esso esca dal proprio ambito di validità, come illustrato nell'esempio seguente.
void SmartPointerDemo2()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Free the memory before we exit function block.
pLarge.reset();
// Do some other work...
}
I puntatori intelligenti consentono in genere di accedere direttamente al relativo puntatore non elaborato. I puntatori intelligenti della libreria standard C++ dispongono di una funzione membro get a questo scopo.
CComPtr ha un membro della classe pubblica p . Fornendo l'accesso diretto al puntatore sottostante, è possibile usare il puntatore intelligente per gestire la memoria nel codice e passare comunque il puntatore non elaborato al codice che non supporta puntatori intelligenti.
void SmartPointerDemo4()
{
// Create the object and pass it to a smart pointer
std::unique_ptr<LargeObject> pLarge(new LargeObject());
//Call a method on the object
pLarge->DoSomething();
// Pass raw pointer to a legacy API
LegacyLargeObjectFunction(pLarge.get());
}
Tipi di puntatori intelligenti
Nella sezione seguente vengono riepilogati i diversi tipi di puntatori intelligenti disponibili nell'ambiente di programmazione Windows e ne viene descritto l'utilizzo.
Puntatori intelligenti della libreria standard C++
Utilizzare questi puntatori intelligenti come scelta principale per incapsulare puntatori a semplici oggetti C++ tradizionali (POCO, plain old C++ objects).
unique_ptrConsente un solo proprietario per il puntatore sottostante. Utilizzalo come scelta predefinita per POCO, a meno che tu non sappia con certezza che ti serve un
shared_ptr. Può essere spostato a un nuovo proprietario, ma non copiato o condiviso. Sostituisceauto_ptr, che è deprecato. Confrontare conboost::scoped_ptr.unique_ptrè piccolo ed efficiente. La dimensione è un puntatore e supporta riferimenti rvalue per l'inserimento e il recupero rapidi dalle raccolte di librerie standard C++. File di intestazione:<memory>. Per altre informazioni, vedere Procedura: Creare e usare istanze di unique_ptr e classe unique_ptr.shared_ptrPuntatore intelligente con conteggio dei riferimenti. Usare quando si vuole assegnare un puntatore non elaborato a più proprietari. Ad esempio, è possibile restituire una copia di un puntatore da un contenitore, ma mantenere anche l'originale. Il puntatore raw non viene eliminato finché tutti i proprietari di
shared_ptrnon escono dall'ambito o non rinunciano in altro modo alla proprietà.La dimensione è pari a due puntatori: uno per l'oggetto e uno per il blocco di controllo condiviso che contiene il contatore di riferimenti. File di intestazione:
<memory>. Per altre informazioni, vedere Procedura: Creare e usare istanze di shared_ptr e classe shared_ptr.weak_ptrPuntatore intelligente specifico da utilizzare con
shared_ptr. Unweak_ptrfornisce l'accesso a un oggetto posseduto da una o più istanze dishared_ptr, ma non partecipa al conteggio dei riferimenti. Usare quando si vuole osservare un oggetto, ma non è necessario che rimanga attivo. Necessario in alcuni casi per interrompere i riferimenti circolari tra istanze dishared_ptr.File di intestazione:
<memory>. Per altre informazioni, vedere Procedura: Creare e usare istanze di weak_ptr e classe weak_ptr.
Puntatori intelligenti per gli oggetti COM (programmazione Classica di Windows)
Quando si utilizzano oggetti COM, racchiudere i puntatori di interfaccia in un tipo appropriato di smart pointer. La libreria ATL (Active Template Library) definisce diversi puntatori intelligenti che assolvono a funzioni diverse. È anche possibile utilizzare il tipo di puntatore intelligente _com_ptr_t, utilizzato dal compilatore durante la creazione di classi wrapper da file TLB. È la scelta migliore se non si desidera includere i file header ATL.
-
Usare questa classe a meno che non sia possibile usare ATL. Esegue il conteggio dei riferimenti mediante i metodi
AddRefeRelease. Per altre informazioni, vedere Procedura: Creare e usare istanze CComPtr e CComQIPtr. -
È simile a
CComPtr, ma fornisce una sintassi semplificata per chiamareQueryInterfacesu oggetti COM. Per altre informazioni, vedere Procedura: Creare e usare istanze CComPtr e CComQIPtr. -
Puntatore intelligente per oggetti che utilizzano
CoTaskMemFreeper liberare la memoria. -
Puntatore intelligente per interfacce ottenute dalla tabella di interfaccia globale (GIT).
-
È simile a
CComQIPtrnelle funzionalità, ma non dipende dagli header ATL.
Puntatori intelligenti ATL per oggetti POCO
Oltre ai puntatori intelligenti per gli oggetti COM, ATL definisce anche puntatori intelligenti e raccolte di puntatori intelligenti per semplici oggetti C++ (POCO). Nella programmazione classica Windows questi tipi sono utili alternative alle raccolte di librerie standard C++, soprattutto quando la portabilità del codice non è necessaria o quando non si vogliono combinare i modelli di programmazione della libreria standard C++ e ATL.
-
Puntatore intelligente che applica la proprietà univoca trasferendo la proprietà sulla copia. Paragonabile alla classe
std::auto_ptrdeprecata. -
Puntatore intelligente per gli oggetti allocati tramite la funzione C malloc .
-
Puntatore intelligente per le matrici allocate tramite
new[]. -
Classe che incapsula una matrice di elementi
CAutoPtr. -
Classe che incapsula metodi per modificare un elenco di nodi
CAutoPtr.