Domanda

Che cosa è un puntatore intelligente e quando devo usare uno?

È stato utile?

Soluzione

Aggiorna

Questa risposta è piuttosto vecchia e descrive quindi ciò che era "buono" al momento, ovvero i suggerimenti intelligenti forniti dalla libreria Boost. A partire da C ++ 11, la libreria standard ha fornito tipi di puntatori intelligenti sufficienti, quindi dovresti favorire l'uso di std::unique_ptr , std::shared_ptr e std::weak_ptr .

Vi è anche std::auto_ptr . È molto simile a un puntatore con ambito, tranne per il fatto che ha anche & Quot; special & Quot; pericolosa capacità di essere copiato & # 8212; che inaspettatamente trasferisce la proprietà! È obsoleto negli standard più recenti, quindi non dovresti usarlo. Utilizza invece il boost::scoped_ptr .

std::auto_ptr<MyObject> p1 (new MyObject());
std::auto_ptr<MyObject> p2 = p1; // Copy and transfer ownership. 
                                 // p1 gets set to empty!
p2->DoSomething(); // Works.
p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.

RISPOSTA VECCHIA

Un puntatore intelligente è una classe che avvolge un puntatore C ++ 'grezzo' (o 'nudo'), per gestire la durata dell'oggetto a cui si punta. Non esiste un singolo tipo di puntatore intelligente, ma tutti cercano di astrarre un puntatore non elaborato in modo pratico.

I puntatori intelligenti dovrebbero essere preferiti ai puntatori non elaborati. Se ritieni di dover usare i puntatori (prima considera se lo fai davvero ), normalmente vorresti usare un puntatore intelligente poiché questo può alleviare molti dei problemi con i puntatori grezzi, dimenticando principalmente di eliminare il oggetto e perdita di memoria.

Con puntatori non elaborati, il programmatore deve distruggere esplicitamente l'oggetto quando non è più utile.

// Need to create the object to achieve some goal
MyObject* ptr = new MyObject(); 
ptr->DoSomething(); // Use the object in some way
delete ptr; // Destroy the object. Done with it.
// Wait, what if DoSomething() raises an exception...?

Un puntatore intelligente al confronto definisce una politica su quando l'oggetto viene distrutto. Devi ancora creare l'oggetto, ma non devi più preoccuparti di distruggerlo.

SomeSmartPtr<MyObject> ptr(new MyObject());
ptr->DoSomething(); // Use the object in some way.

// Destruction of the object happens, depending 
// on the policy the smart pointer class uses.

// Destruction would happen even if DoSomething() 
// raises an exception

La politica più semplice in uso riguarda l'ambito dell'oggetto wrapper puntatore intelligente, come implementato da boost::shared_ptr o weak_ptr .

void f()
{
    {
       std::unique_ptr<MyObject> ptr(new MyObject());
       ptr->DoSomethingUseful();
    } // ptr goes out of scope -- 
      // the MyObject is automatically destroyed.

    // ptr->Oops(); // Compile error: "ptr" not defined
                    // since it is no longer in scope.
}

Nota che shared_ptr le istanze non possono essere copiate. Ciò impedisce che il puntatore venga eliminato più volte (in modo errato). Tuttavia, puoi passare riferimenti ad esso ad altre funzioni che chiami.

<=> s sono utili quando si desidera associare la durata dell'oggetto a un particolare blocco di codice o se lo si è incorporato come dati membro all'interno di un altro oggetto, la durata di quell'altro oggetto. L'oggetto esiste fino all'uscita dal blocco di codice contenente, o fino a quando l'oggetto contenitore non viene distrutto.

Una politica di puntatore intelligente più complessa prevede il conteggio dei riferimenti del puntatore. Ciò consente di copiare il puntatore. Quando l'ultimo & Quot; riferimento & Quot; all'oggetto viene distrutto, l'oggetto viene eliminato. Questa politica è implementata da <=> e < a href = "http://en.cppreference.com/w/cpp/memory/shared_ptr" rel = "nofollow noreferrer"> <=> .

void f()
{
    typedef std::shared_ptr<MyObject> MyObjectPtr; // nice short alias
    MyObjectPtr p1; // Empty

    {
        MyObjectPtr p2(new MyObject());
        // There is now one "reference" to the created object
        p1 = p2; // Copy the pointer.
        // There are now two references to the object.
    } // p2 is destroyed, leaving one reference to the object.
} // p1 is destroyed, leaving a reference count of zero. 
  // The object is deleted.

I puntatori contati di riferimento sono molto utili quando la durata del tuo oggetto è molto più complicata e non è legata direttamente a una particolare sezione di codice o ad un altro oggetto.

C'è un inconveniente per fare riferimento ai puntatori contati & # 8212; la possibilità di creare un riferimento penzolante:

// Create the smart pointer on the heap
MyObjectPtr* pp = new MyObjectPtr(new MyObject())
// Hmm, we forgot to destroy the smart pointer,
// because of that, the object is never destroyed!

Un'altra possibilità è la creazione di riferimenti circolari:

struct Owner {
   std::shared_ptr<Owner> other;
};

std::shared_ptr<Owner> p1 (new Owner());
std::shared_ptr<Owner> p2 (new Owner());
p1->other = p2; // p1 references p2
p2->other = p1; // p2 references p1

// Oops, the reference count of of p1 and p2 never goes to zero!
// The objects are never destroyed!

Per aggirare questo problema, sia Boost che C ++ 11 hanno definito un <=> per definire un riferimento debole (non contato) a un <=>.

Altri suggerimenti

Ecco una semplice risposta per questi giorni del moderno C ++:

  • Che cos'è un puntatore intelligente?
    È un tipo i cui valori possono essere usati come puntatori, ma che fornisce la funzionalità aggiuntiva della gestione automatica della memoria: quando un puntatore intelligente non è più in uso, la memoria a cui punta è deallocata (vedi anche la definizione più dettagliata su Wikipedia ).
  • Quando dovrei usarne uno?
    Nel codice che prevede il monitoraggio della proprietà di un pezzo di memoria, l'allocazione o la disallocazione; il puntatore intelligente spesso ti fa risparmiare la necessità di fare esplicitamente queste cose.
  • Ma quale puntatore intelligente dovrei usare in quale di questi casi?
    • Utilizza std::unique_ptr quando non intendi conservare più riferimenti allo stesso oggetto. Ad esempio, usalo per un puntatore alla memoria che viene allocato quando entri in un ambito e disalloca quando esce dall'ambito.
    • Utilizza std::shared_ptr quando desideri fare riferimento al tuo oggetto da più luoghi - e non vuoi che il tuo oggetto venga allocato fino a quando tutti questi riferimenti non saranno scomparsi.
    • Utilizza std::weak_ptr quando desideri fare riferimento al tuo oggetto da luoghi multipli - per quei riferimenti per i quali è ok ignorare e deallocare (quindi noteranno solo che l'oggetto è sparito quando provi a dereferenziare).
    • Non utilizzare i boost:: puntatori intelligenti o std::auto_ptr tranne in casi speciali sui quali è possibile leggere se necessario.
  • Ehi, non ho chiesto quale usare!
    Ah, ma lo volevi davvero, ammettilo.
  • Quindi, quando dovrei usare puntatori regolari allora?
    Principalmente nel codice che è ignaro della proprietà della memoria. Ciò si verifica generalmente in funzioni che ottengono un puntatore da qualche altra parte e non allocano né disallocano e non memorizzano una copia del puntatore che sopravvive alla loro esecuzione.

Puntatore intelligente è un tipo simile a un puntatore con alcune funzionalità aggiuntive, ad es. deallocazione automatica della memoria, conteggio dei riferimenti ecc.

La piccola introduzione è disponibile a pagina Puntatori intelligenti - Cosa, perché, quale? .

Uno del tipo di puntatore intelligente semplice è std::auto_ptr (capitolo 20.4.5 dello standard C ++), che consente di deallocare automaticamente la memoria quando non rientra nell'ambito e che è più affidabile del semplice utilizzo del puntatore quando vengono generate eccezioni, sebbene meno flessibile.

Un altro tipo conveniente è boost::shared_ptr che implementa il conteggio dei riferimenti e alloca automaticamente la memoria quando non rimane alcun riferimento all'oggetto. Questo aiuta a evitare perdite di memoria ed è facile da usare per implementare RAII .

L'argomento è approfondito nel libro " Modelli C ++: la guida completa " di David Vandevoorde, Nicolai M. Josuttis , capitolo 20. Puntatori intelligenti. Alcuni argomenti trattati:

Le definizioni fornite da Chris, Sergdev e Llyod sono corrette. Preferisco una definizione più semplice, però, solo per semplificarmi la vita: Un puntatore intelligente è semplicemente una classe che sovraccarica gli operatori -> e *. Il che significa che il tuo oggetto sembra semanticamente un puntatore ma puoi farlo fare cose più interessanti, tra cui il conteggio dei riferimenti, la distruzione automatica ecc. shared_ptr e auto_ptr sono sufficienti nella maggior parte dei casi, ma si presentano con il proprio set di piccole idiosincrasie.

Un puntatore intelligente è come un normale puntatore (tipizzato), come " char * " ;, tranne quando il puntatore stesso esce dall'ambito di applicazione, anche quello a cui punta viene eliminato. Puoi usarlo come faresti con un normale puntatore, usando & Quot; - & Gt; & Quot ;, ma non se hai bisogno di un vero puntatore ai dati. Per questo, puoi usare & Quot; & Amp; * ptr & Quot ;.

È utile per:

  • Oggetti che devono essere allocati con nuovi, ma che vorresti avere la stessa durata di qualcosa nello stack. Se l'oggetto viene assegnato a un puntatore intelligente, verranno eliminati quando il programma esce da quella funzione / blocco.

  • Membri dei dati delle classi, in modo che quando l'oggetto viene eliminato anche tutti i dati posseduti vengano eliminati, senza alcun codice speciale nel distruttore (sarà necessario assicurarsi che il distruttore sia virtuale, che è quasi sempre una buona cosa da fare).

Puoi non usare un puntatore intelligente quando:

  • ... il puntatore non dovrebbe in realtà possedere i dati ... vale a dire, quando si stanno semplicemente usando i dati, ma si desidera che sopravvivano alla funzione in cui si fa riferimento.
  • ... il puntatore intelligente non verrà distrutto a un certo punto. Non vuoi che rimanga nella memoria che non viene mai distrutta (come in un oggetto allocato in modo dinamico ma che non verrà eliminato esplicitamente).
  • ... due puntatori intelligenti potrebbero puntare agli stessi dati. (Esistono, tuttavia, puntatori ancora più intelligenti che gestiranno questo ... che si chiama conteggio dei riferimenti .)

Vedi anche:

La maggior parte dei tipi di puntatori intelligenti gestiscono lo smaltimento dell'oggetto pointer-to. È molto utile perché non devi più pensare a smaltire gli oggetti manualmente.

I puntatori intelligenti più comunemente usati sono std::tr1::shared_ptr (o boost::shared_ptr) e, meno comunemente, std::auto_ptr. Consiglio l'uso regolare di shared_ptr.

libc è molto versatile e si occupa di una grande varietà di scenari di smaltimento, inclusi i casi in cui gli oggetti devono essere " passati attraverso i limiti DLL " (il caso da incubo comune se vengono utilizzati <=> s diversi tra il codice e le DLL).

Un puntatore intelligente è un oggetto che agisce come un puntatore, ma fornisce inoltre controllo su costruzione, distruzione, copia, spostamento e dereferenziazione.

È possibile implementare il proprio puntatore intelligente, ma molte librerie forniscono anche implementazioni di puntatori intelligenti, ognuna con vantaggi e svantaggi diversi.

Ad esempio, Boost fornisce le seguenti implementazioni di puntatori intelligenti:

  • shared_ptr<T> è un puntatore a T utilizzando un conteggio di riferimento per determinare quando l'oggetto non è più necessario.
  • scoped_ptr<T> è un puntatore eliminato automaticamente quando esce dall'ambito. Nessun incarico è possibile.
  • intrusive_ptr<T> è un altro puntatore di conteggio dei riferimenti. Offre prestazioni migliori rispetto a shared_ptr, ma richiede il tipo weak_ptr<T> per fornire il proprio meccanismo di conteggio dei riferimenti.
  • shared_array<T> è un puntatore debole, che funziona insieme a scoped_array<T> per evitare riferimenti circolari.
  • scoped_ptr è come std::unique_ptr, ma per array di std::shared_ptr.
  • std::weak_ptr è come std::auto_ptr, ma per array di <=>.

Queste sono solo una descrizione lineare di ciascuna e possono essere utilizzate secondo le necessità, per ulteriori dettagli ed esempi è possibile consultare la documentazione di Boost.

Inoltre, la libreria standard C ++ fornisce tre puntatori intelligenti; <=> per proprietà unica, <=> per proprietà condivisa e <=>. <=> esisteva in C ++ 03 ma ora è obsoleto.

Ecco il link per risposte simili: http: //sickprogrammersarea.blogspot .in / 2014/03 / tecnico-intervista-domande-on-c_6.html

Un puntatore intelligente è un oggetto che agisce, appare e si sente come un normale puntatore ma offre più funzionalità. In C ++, i puntatori intelligenti sono implementati come classi di modelli che incapsulano un puntatore e sovrascrivono gli operatori di puntatori standard. Hanno una serie di vantaggi rispetto ai puntatori regolari. Sono garantiti per essere inizializzati come puntatori null o puntatori a un oggetto heap. L'indirizzamento tramite un puntatore null è verificato. Nessuna cancellazione è mai necessaria. Gli oggetti vengono liberati automaticamente quando l'ultimo puntatore è scomparso. Un problema significativo con questi puntatori intelligenti è che, a differenza dei puntatori normali, non rispettano l'eredità. I puntatori intelligenti non sono attraenti per il codice polimorfico. Di seguito è riportato un esempio per l'implementazione di puntatori intelligenti.

Esempio:

template <class X>
class smart_pointer
{
          public:
               smart_pointer();                          // makes a null pointer
               smart_pointer(const X& x)            // makes pointer to copy of x

               X& operator *( );
               const X& operator*( ) const;
               X* operator->() const;

               smart_pointer(const smart_pointer <X> &);
               const smart_pointer <X> & operator =(const smart_pointer<X>&);
               ~smart_pointer();
          private:
               //...
};

Questa classe implementa un puntatore intelligente su un oggetto di tipo X. L'oggetto stesso si trova sull'heap. Ecco come usarlo:

smart_pointer <employee> p= employee("Harris",1333);

Come altri operatori sovraccarichi, p si comporterà come un normale puntatore,

cout<<*p;
p->raise_salary(0.5);

http://en.wikipedia.org/wiki/Smart_pointer

  

In informatica, un puntatore intelligente   è un tipo di dati astratto che   simula un puntatore fornendo   funzionalità aggiuntive, come automatica   raccolta dei rifiuti o controllo dei limiti.   Queste funzionalità aggiuntive sono destinate   per ridurre i bug causati dall'uso improprio di   puntatori mantenendo l'efficienza.   I puntatori intelligenti in genere tengono traccia di   gli oggetti che puntano ad essi per il   scopo della gestione della memoria. Il   l'uso improprio di puntatori è una fonte importante   di bug: l'allocazione costante,   deallocazione e referenziazione che devono   essere eseguito da un programma scritto   l'utilizzo di puntatori lo rende molto probabile   che si verificheranno alcune perdite di memoria.   I puntatori intelligenti cercano di prevenire la memoria   perde facendo la risorsa   deallocazione automatica: quando il   puntatore a un oggetto (o l'ultimo in a   serie di puntatori) viene distrutto, per   esempio perché va fuori dal campo di applicazione,   anche l'oggetto appuntito viene distrutto.

Sia T una classe in questo tutorial Puntatori in C++ può essere diviso in 3 tipi :

1) Raw puntatori :

T a;  
T * _ptr = &a; 

Sono in possesso di un indirizzo di memoria di un percorso nella memoria.Usare con cautela , come i programmi di diventare complesso difficile tenere traccia.

Puntatori con const dati o indirizzo { Leggi indietro }

T a ; 
const T * ptr1 = &a ; 
T const * ptr1 = &a ;

Puntatore a un tipo di dati T che è un const.Il significato non è possibile modificare il tipo di dati utilizzando il puntatore del mouse.ie *ptr1 = 19 ;non funziona.Ma è possibile spostare il puntatore.ie ptr1++ , ptr1-- ;ecc di lavoro.Letto al contrario :puntatore di tipo T, che è const

  T * const ptr2 ;

Un puntatore const a un tipo di dati T .Significa che è possibile spostare il puntatore, ma è possibile modificare il valore a cui punta il puntatore.ie *ptr2 = 19 funzionerà, ma ptr2++ ; ptr2-- ecc non funziona.Letto al contrario :const puntatore a un tipo di T

const T * const ptr3 ; 

Un puntatore const a const T tipo di dati .Il significato non è possibile spostare il puntatore non è possibile modificare il tipo di dati puntatore a puntatore.ie . ptr3-- ; ptr3++ ; *ptr3 = 19; non funziona

3) Puntatori Intelligenti : { #include <memory> }

Puntatore Condiviso:

  T a ; 
     //shared_ptr<T> shptr(new T) ; not recommended but works 
     shared_ptr<T> shptr = make_shared<T>(); // faster + exception safe

     std::cout << shptr.use_count() ; // 1 //  gives the number of " 
things " pointing to it. 
     T * temp = shptr.get(); // gives a pointer to object

     // shared_pointer used like a regular pointer to call member functions
      shptr->memFn();
     (*shptr).memFn(); 

    //
     shptr.reset() ; // frees the object pointed to be the ptr 
     shptr = nullptr ; // frees the object 
     shptr = make_shared<T>() ; // frees the original object and points to new object

Implementato utilizzando il conteggio dei riferimenti per tenere traccia di quante "cose", scegliere l'oggetto a cui punta il puntatore.Quando il conteggio va a 0 , l'oggetto viene eliminato automaticamente , vale a dire contestato viene eliminato quando tutti i share_ptr indicando l'oggetto esce dall'ambito.Questo elimina il mal di testa di dover eliminare gli oggetti che hai allocato con new.

Debole Puntatore : Aiuta a trattare con riferimento ciclico che si verifica quando usando il Puntatore Condiviso Se si dispone di due oggetti a cui punta due condivisa puntatori e c'è un interno condivisa puntatore che punta ad altri puntatore condiviso poi ci sarà una ciclica di riferimento e l'oggetto non verrà cancellato se condivisa puntatori andare fuori del campo di applicazione.Per risolvere questo problema , modificare il membro interno da un shared_ptr per weak_ptr.Nota :Per accedere all'elemento puntato da un debole puntatore lock () restituisce un weak_ptr.

T a ; 
shared_ptr<T> shr = make_shared<T>() ; 
weak_ptr<T> wk = shr ; // initialize a weak_ptr from a shared_ptr 
wk.lock()->memFn() ; // use lock to get a shared_ptr 
//   ^^^ Can lead to exception if the shared ptr has gone out of scope
if(!wk.expired()) wk.lock()->memFn() ;
// Check if shared ptr has gone out of scope before access

Vedere : Quando è std::weak_ptr utile?

Unico Puntatore : Il peso leggero smart pointer con la proprietà esclusiva.Utilizzare quando il puntatore punta a oggetti unici, senza la condivisione di oggetti tra i puntatori.

unique_ptr<T> uptr(new T);
uptr->memFn(); 

//T * ptr = uptr.release(); // uptr becomes null and object is pointed to by ptr
uptr.reset() ; // deletes the object pointed to by uptr 

Per modificare l'oggetto a cui punta l'unico ptr , utilizzare la semantica di spostamento

unique_ptr<T> uptr1(new T);
unique_ptr<T> uptr2(new T);
uptr2 = std::move(uptr1); 
// object pointed by uptr2 is deleted and 
// object pointed by uptr1 is pointed to by uptr2
// uptr1 becomes null 

Riferimenti :Essi possono essere essenzialmente anche se, come const puntatori, cioè un puntatore che è costante e non può essere spostato con una sintassi migliore.

Vedere : Quali sono le differenze tra un puntatore a variabile e una variabile di riferimento in C++?

r-value reference : reference to a temporary object   
l-value reference : reference to an object whose address can be obtained
const reference : reference to a data type which is const and cannot be modified 

Riferimento :https://www.youtube.com/channel/UCEOGtxYTB6vo6MQ-WQ9W_nQ Grazie ad Andre per aver ricordato questa domanda.

Un puntatore intelligente è una classe, un wrapper di un puntatore normale. A differenza dei normali puntatori, il punto vita di smart point & # 8217; si basa su un conteggio di riferimento (quante volte viene assegnato l'oggetto puntatore intelligente). Pertanto, ogni volta che un puntatore intelligente viene assegnato a un altro, il riferimento interno conta più più. E ogni volta che l'oggetto esce dal campo di applicazione, il conteggio dei riferimenti meno meno.

Il puntatore automatico, sebbene sembri simile, è totalmente diverso dal puntatore intelligente. È una classe conveniente che dealloca la risorsa ogni volta che un oggetto puntatore automatico esce dall'ambito variabile. In una certa misura, fa funzionare un puntatore (alla memoria allocata dinamicamente) in modo simile a una variabile di stack (allocata staticamente in tempo di compilazione).

I puntatori intelligenti sono quelli in cui non devi preoccuparti della disallocazione della memoria, della condivisione delle risorse e del trasferimento.

Puoi benissimo usare questi puntatori nello stesso modo in cui qualsiasi allocazione funziona in Java. In java Garbage Collector fa il trucco, mentre in Smart Pointers il trucco è fatto dai Distruttori.

Le risposte esistenti sono buone ma non coprono cosa fare quando un puntatore intelligente non è la risposta (completa) al problema che si sta tentando di risolvere.

Tra l'altro (spiegato bene in altre risposte) l'uso di un puntatore intelligente è una possibile soluzione per Come possiamo usare una classe astratta come tipo di ritorno di funzione? che è stato contrassegnato come duplicato di questa domanda. Tuttavia, la prima domanda da porsi se si è tentati di specificare una classe base astratta (o di fatto una qualsiasi) come tipo di ritorno in C ++ è & Quot; cosa intendi veramente? & Quot ;. C'è una buona discussione (con ulteriori riferimenti) sulla programmazione orientata agli oggetti idiomatica in C ++ (e come questo è diverso da altri linguaggi) nella documentazione di aumenta la libreria contenitore puntatore . In sintesi, in C ++ devi pensare alla proprietà. Quali smart pointer ti aiutano, ma non sono l'unica soluzione, o sempre una soluzione completa (non ti danno una copia polimorfica) e non sono sempre una soluzione che vuoi esporre nella tua interfaccia (e una funzione di ritorno suona terribile molto simile a un'interfaccia). Potrebbe essere sufficiente restituire un riferimento, ad esempio. Ma in tutti questi casi (puntatore intelligente, contenitore puntatore o semplicemente restituendo un riferimento) hai cambiato il ritorno da un valore a una qualche forma di riferimento . Se hai davvero bisogno di una copia potresti dover aggiungere più boilerplate & Quot; idiom & Quot; o andare oltre OOP idiomatico (o altrimenti) in C ++ a polimorfismo più generico usando librerie come Adobe Poly o Boost.TypeErasure .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top