2014-07-16 13 views
25

mi interessa se queste due righe di codice sono gli stessi:Cosa succede quando si usa make_shared

shared_ptr<int> sp(new int(1)); // double allocation? 
shared_ptr<int> sp(make_shared<int>(1)); // just one allocation? 

Se questo è vero che qualcuno potrebbe spiegare perché è solo un'allocazione nella seconda riga?

+4

Per essere chiari, non è una doppia allocazione di 'int'. Sono solo due allocazioni separate: una per l'oggetto 'int' e un'altra per il blocco di controllo' shared_ptr'. La seconda riga è solo una singola allocazione sia del 'int' che del blocco di controllo in un colpo solo. –

+2

vedere il punto 2 qui: http://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/ – perreal

+1

Nel secondo caso 'make_shared' alloca sia il' int' che il blocco di controllo ed è quindi libero di allocare entrambi in una volta. Nel primo caso allocate 'int' e il costruttore di' shared_ptr' alloca il blocco di controllo e non c'è modo di unire le allocazioni. – nwp

risposta

31

Il primo caso non esegue una doppia assegnazione, esegue due allocazioni, uno per l'oggetto gestito e uno per il blocco controllo del shared_ptr.

Per il secondo caso, cppreference ha una buona spiegazione del perché std::make_sharedsolito esegui solo un'allocazione di memoria si dice (sottolineatura mia andando avanti):

Questa funzione alloca tipicamente memoria per il T oggetto e per il blocco di controllo shared_ptr con una singola allocazione di memoria (è un requisito non vincolante nello standard nello standard). Al contrario, la dichiarazione std :: shared_ptr p (new T (Args ...)) esegue almeno due allocazioni di memoria , che potrebbero comportare inutili spese generali.

e da std::shared_ptr sezione che dice: si crea

Quando shared_ptr viene creato chiamando std :: make_shared o std :: allocate_shared, la memoria sia per il blocco di controllo e l' oggetto gestito con una singola allocazione. L'oggetto gestito viene creato sul posto in un membro dati del blocco di controllo. Quando shared_ptr viene creato tramite uno dei costruttori shared_ptr, l'oggetto gestito e il blocco di controllo deve essere assegnato separatamente. Nel caso , il blocco di controllo memorizza un puntatore all'oggetto gestito.

Questa descrizione make_shared è coerente con la C++11 draft standard che dice nella sezione 20.7.2.2.6 creazione shared_ptr

template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args); 
template<class T, class A, class... Args> 
    shared_ptr<T> allocate_shared(const A& a, Args&&... args); 

[...]

Osservazione: Implementazioni dovrebbe eseguire non più di un memoria allocazione. [Nota: questo fornisce l'efficienza equivalente a un puntatore intelligente intrusivo . -end note]

[Nota: in genere queste funzioni allocano più memoria di sizeof (T) per consentire le strutture di contabilità interna come i conteggi di riferimento .-end nota]

Herb Sutter ha una spiegazione più dettagliata dei vantaggi di utilizzare make_shared in GotW #89 Solution: Smart Pointers ed evidenzia alcuni vantaggi:

  • Si riduce assegnazione sovraccarico
  • Migliora frazione.
  • Evita un nuovo esplicito.
  • Evita un problema di sicurezza delle eccezioni.

Tenere presente che quando si utilizza std::weak_ptrusing make_shared has some disadvantages.

4

Spiegazione da cppreference std::shared_ptr in Implementation notes sezione

In una tipica implementazione, std :: shared_ptr vale soltanto due punti:

  1. un puntatore all'oggetto gestito
  2. un puntatore per controllare blocco

Quando shared_ptr viene creato chiamando std :: make_shared o std :: allocate_shared, la memoria per il blocco di controllo e l'oggetto gestito viene creata con una singola allocazione. L'oggetto gestito viene creato sul posto in un membro dati del blocco di controllo. Quando shared_ptr viene creato tramite uno dei costruttori shared_ptr, l'oggetto gestito e il blocco di controllo deve essere assegnato separatamente. Nel caso , il blocco di controllo memorizza un puntatore all'oggetto gestito.

0

C'è anche un potenziale bug sottile: in sp(new int) si FIST allocare un int (il cui puntatore è dato a sp), che a sua volta ha sp per allocare un blocco di controllo (conterrà i contatori e il deleter).

Ora, se si esegue l'ultima allocazione sp non riesce (memoria insufficiente) si rimane con un heap assegnato int il cui puntatore non è tenuto da nessuno e quindi impossibile da eliminare. (perdita di memoria).