Esempio per usare shared_ptr?

Ciao ho fatto una domanda oggi su Come inserire diversi tipi di oggetti nella stessa matrice vettoriale e il mio codice in quella domanda era

gate* G[1000]; G[0] = new ANDgate() ; G[1] = new ORgate; //gate is a class inherited by ANDgate and ORgate classs class gate { ..... ...... virtual void Run() { //A virtual function } }; class ANDgate :public gate {..... ....... void Run() { //AND version of Run } }; class ORgate :public gate {..... ....... void Run() { //OR version of Run } }; //Running the simulator using overloading concept for(...;...;..) { G[i]->Run() ; //will run perfectly the right Run for the right Gate type } 

e volevo usare i vettori così qualcuno ha scritto che dovevo farlo:

 std::vector G; G.push_back(new ANDgate); G.push_back(new ORgate); for(unsigned i=0;iRun(); } 

ma poi lui e molti altri hanno suggerito che avrei usato meglio i contenitori di puntatori Boost
o shared_ptr . Ho passato le ultime 3 ore a leggere su questo argomento, ma la documentazione mi sembra abbastanza avanzata. **** Qualcuno può darmi un piccolo esempio di codice di utilizzo shared_ptr e perché hanno suggerito l’uso di shared_ptr . Ci sono anche altri tipi come ptr_vector , ptr_list e ptr_deque ** **

Edit1: Ho letto anche un esempio di codice che includeva:

 typedef boost::shared_ptr FooPtr; ....... int main() { std::vector foo_vector; ........ FooPtr foo_ptr( new Foo( 2 ) ); foo_vector.push_back( foo_ptr ); ........... } 

E non capisco la syntax!

L’utilizzo di un vector di shared_ptr rimuove la possibilità di perdite di memoria perché hai dimenticato di camminare sul vettore e chiamare delete su ciascun elemento. Passiamo attraverso una versione leggermente modificata dell’esempio linea per linea.

 typedef boost::shared_ptr gate_ptr; 

Creare un alias per il tipo di puntatore condiviso. Ciò evita la bruttezza del linguaggio C ++ risultante dalla digitazione di std::vector > e dimenticando lo spazio tra i segni di maggiore- chiusura.

  std::vector vec; 

Crea un vettore vuoto di boost::shared_ptr oggetti.

  gate_ptr ptr(new ANDgate); 

ANDgate una nuova istanza di ANDgate e memorizzala in una shared_ptr . La ragione per fare questo separatamente è per prevenire un problema che può verificarsi se un’operazione getta. Questo non è ansible in questo esempio. Il Boost shared_ptr “Best Practices” spiega perché è una buona pratica allocare in un object indipendente invece che temporaneo.

  vec.push_back(ptr); 

Questo crea un nuovo puntatore condiviso nel vettore e copia ptr in esso. Il conteggio dei riferimenti nell’intestino di shared_ptr garantisce che l’object allocato all’interno di ptr sia trasferito in modo sicuro nel vettore.

Ciò che non è spiegato è che il distruttore per shared_ptr assicura che la memoria allocata sia cancellata. È qui che viene evitata la perdita di memoria. Il distruttore per std::vector assicura che il distruttore per T venga chiamato per ogni elemento memorizzato nel vettore. Tuttavia, il distruttore per un puntatore (ad es. gate* ) non cancella la memoria allocata . Questo è quello che stai cercando di evitare usando shared_ptr o ptr_vector .

Aggiungerò che una delle cose importanti su shared_ptr ‘s è di costruirle sempre e solo con la seguente syntax:

 shared_ptr(new Type(...)); 

In questo modo, il puntatore “reale” di Type è anonimo per il tuo ambito e viene trattenuto solo dal puntatore condiviso. Quindi sarà imansible per te utilizzare accidentalmente questo puntatore “reale”. In altre parole, non farlo mai:

 Type* t_ptr = new Type(...); shared_ptr t_sptr ptrT(t_ptr); //t_ptr is still hanging around! Don't use it! 

Anche se funzionerà, ora hai un puntatore Type* ( t_ptr ) nella tua funzione che vive al di fuori del puntatore condiviso. È pericoloso usare t_ptr ovunque, perché non si sa mai quando il puntatore condiviso che lo contiene può distruggerlo e si segfault.

Lo stesso vale per i suggerimenti restituiti da altre classi. Se un corso non ti ha scritto un puntatore, in genere non è sicuro inserirlo in una shared_ptr . No, a meno che tu non sia sicuro che la class non stia più usando quell’object. Perché se lo metti in un shared_ptr , e non rientra nell’ambito, l’object verrà liberato quando la class potrebbe ancora averne bisogno.

Imparare a usare i puntatori intelligenti è, a mio parere, uno dei passi più importanti per diventare un programmatore C ++ competente. Come sai ogni volta che crei un nuovo object, a un certo punto lo vuoi eliminare.

Un problema che si presenta è che con le eccezioni può essere molto difficile assicurarsi che un object venga sempre rilasciato una sola volta in tutti i possibili percorsi di esecuzione.

Questa è la ragione per RAII: http://en.wikipedia.org/wiki/RAII

Creare una class helper allo scopo di assicurarsi che un object venga sempre eliminato una volta in tutti i percorsi di esecuzione.

Esempio di una class come questa è: std :: auto_ptr

Ma a volte ti piace condividere oggetti con altri. Dovrebbe essere cancellato solo quando nessuno lo usa più.

Per aiutare con quel riferimento, sono state sviluppate strategie di conteggio ma è comunque necessario ricordare addref e rilasciare ref manualmente. In sostanza, questo è lo stesso problema di nuovo / cancella.

Ecco perché boost ha sviluppato boost :: shared_ptr, è il conteggio dei riferimenti del puntatore intelligente in modo da poter condividere oggetti e non perdere memoria involontariamente.

Con l’aggiunta di C ++ tr1 questo è ora aggiunto anche allo standard c ++ ma è denominato std :: tr1 :: shared_ptr <>.

Consiglio di utilizzare il puntatore condiviso standard, se ansible. ptr_list, ptr_dequeue e così sono contenitori specializzati IIRC per i tipi di puntatore. Li ignoro per ora.

Quindi possiamo iniziare dal tuo esempio:

 std::vector G; G.push_back(new ANDgate); G.push_back(new ORgate); for(unsigned i=0;iRun(); } 

Il problema qui è che ogni volta che G esce fuori scope perdiamo i 2 oggetti aggiunti a G. Riscriviamolo per usare std :: tr1 :: shared_ptr

 // Remember to include  for shared_ptr // First do an alias for std::tr1::shared_ptr so we don't have to // type that in every place. Call it gate_ptr. This is what typedef does. typedef std::tr1::shared_ptr gate_ptr; // gate_ptr is now our "smart" pointer. So let's make a vector out of it. std::vector G; // these smart_ptrs can't be implicitly created from gate* we have to be explicit about it // gate_ptr (new ANDgate), it's a good thing: G.push_back(gate_ptr (new ANDgate)); G.push_back(gate_ptr (new ORgate)); for(unsigned i=0;iRun(); } 

Quando G esce dal campo di applicazione, la memoria viene automaticamente recuperata.

Come esercizio che ho afflitto i nuovi arrivati ​​nella mia squadra sta chiedendo loro di scrivere la propria class di puntatore intelligente. Poi, dopo aver finito, scartare immediatamente la class e non usarla mai più. Si spera che tu abbia acquisito una conoscenza cruciale su come funziona un puntatore intelligente sotto il cofano. Non c’è davvero magia.

La documentazione di boost fornisce un buon esempio di avvio: esempio shared_ptr (in realtà si tratta di un vettore di puntatori intelligenti) o doc shared_ptr La seguente risposta di Johannes Schaub spiega piuttosto bene i puntatori intelligenti di potenziamento: spiegazioni intelligenti

L’idea alla base (nel minor numero ansible di parole) ptr_vector è che gestisce la deallocazione della memoria dietro i puntatori memorizzati per te: diciamo che hai un vettore di puntatori come nel tuo esempio. Quando esci dall’applicazione o lasci lo scope in cui è definito il vettore, dovrai ripulire te stesso (hai assegnato in modo dinamico ANDgate e ORgate) ma solo cancellare il vettore non lo farà perché il vettore sta memorizzando i puntatori e non gli oggetti reali (non distruggerà ma ciò che contiene).

  // if you just do G.clear() // will clear the vector but you'll be left with 2 memory leaks ... // to properly clean the vector and the objects behind it for (std::vector::iterator it = G.begin(); it != G.end(); it++) { delete (*it); } 

boost :: ptr_vector <> gestirà quanto sopra per voi, il che significa che trasferirà la memoria dietro i puntatori che memorizza.

Attraverso Boost puoi farlo>

 std::vector vecobj; boost::shared_ptr sharedString1(new string("abcdxyz!")); boost::shared_ptr sharedint1(new int(10)); vecobj.push_back(sharedString1); vecobj.push_back(sharedint1); 

> per l’inserimento di un tipo di object diverso nel contenitore vettoriale. mentre per l’accesso devi usare any_cast, che funziona come dynamic_cast, spera che funzioni per te.

 #include  #include  class SharedMemory { public: SharedMemory(int* x):_capture(x){} int* get() { return (_capture.get()); } protected: std::shared_ptr _capture; }; int main(int , char**){ SharedMemory *_obj1= new SharedMemory(new int(10)); SharedMemory *_obj2 = new SharedMemory(*_obj1); std::cout << " _obj1: " << *_obj1->get() << " _obj2: " << *_obj2->get() << std::endl; delete _obj2; std::cout << " _obj1: " << *_obj1->get() << std::endl; delete _obj1; std::cout << " done " << std::endl; } 

Questo è un esempio di shared_ptr in azione. _obj2 è stato eliminato ma il puntatore è ancora valido. l'output è, ./test _obj1: 10 _obj2: 10 _obj2: 10 done

Il modo migliore per aggiungere oggetti diversi nello stesso contenitore è usare il loop basato su make_shared, vector e range e avrai un codice carino, pulito e “leggibile”!

 typedef std::shared_ptr Ptr vector myConatiner; auto andGate = std::make_shared(); myConatiner.push_back(andGate ); auto orGate= std::make_shared(); myConatiner.push_back(orGate); for (auto& element : myConatiner) element->run();