2012-03-20 31 views
5

I seguenti programma va in crash con un cattivo glibc doppia errori:Cosa succede se reimpostare uno std :: shared_ptr a se stesso

#include <iostream> 
#include <memory> 

class foo { 
public: 
    foo() 
    { 
     std::cout << "foo constructed" << std::endl; 
    } 

    ~foo() 
    { 
     std::cout << "foo destructed" << std::endl; 
    } 
}; 

int main() { 
    auto f = std::make_shared<foo>(); 
    std::cout << "Before reset" << std::endl; 
    f.reset(f.get()); 
    std::cout << "After reset" << std::endl; 
    return 0; 
} 

Da questo ottengo il seguente output (seguita dall'errore di glibc):

foo constructed 
Before reset 
foo destructed 
After reset 
foo destructed 

Quindi, ovviamente, in questo caso l'oggetto viene distrutto due volte. Una volta dal reset e una volta dallo std::shared_ptr uscendo dal campo di applicazione. Questo è in realtà quello che mi sarei aspettato.

Su cppreference tuttavia trovo il seguente testo (che si trova a http://en.cppreference.com/w/cpp/memory/shared_ptr/reset):

If *this already owns an object and it is the last shared_ptr owning it, the object is destroyed through the owned deleter, unless ptr is a pointer to it.

A mio parere questo dice in realtà, che l'oggetto non deve essere distrutta come nel mio esempio. Abbastanza sorprendente, ma se lo standard lo dice. Sono in qualche modo frainteso questo, oppure l'implementazione di std::shared_ptr non è conforme allo standard?

Per coloro che chiedono il motivo per cui sto facendo questo:

Attualmente sto cercando di capire come gestire temporaneamente puntatori nude per oggetti creati da new e new[]. L'idea è di usare lo std::shared_ptr::reset() per sostituire il deleter con un deleter no-op. L'alternativa è di avvolgere il codice con un tipo di blocco try { stuff() } catch(...) { delete x; throw;}.

+2

'f.reset (f.get())' perché dovresti mai * * vuoi fare questo? Se hai "puntatori nudi agli oggetti" temporanei, non dovresti * cancellarli *. Dovresti eliminare solo ciò che * possiedi * e non possiedi ciò che ottieni da 'shared_ptr :: get'. –

+0

@NicolBolas: Risposta facile: poiché cppreference ha suggerito che questo sarebbe stato gestito in modo diverso rispetto ad altri casi. Sembra che l'informazione sia sbagliata. Inizierò una discussione su questo in cppreference. – LiKao

+0

Questo spiega solo perché hai pensato che avrebbe funzionato. Questo non spiega perché hai pensato che sarebbe stata una buona idea, che funzioni o meno. Cosa stai cercando di fare * per * questo * questo? –

risposta

7

La specifica per che il sovraccarico di reset è dato in 20.7.2.2.4 modificatori shared_ptr [util.smartptr.shared.mod], paragrafo 3 (da n3290):

template<class Y> void reset(Y* p); 

Effects: Equivalent to shared_ptr(p).swap(*this) .

Come si può vedere, che la costruzione di shared_ptr(p) crea un nuovo conteggio con un nuovo deleter per quello p, quindi niente di buono può venirne fuori. È davvero meglio pensare a std::shared_ptr<T>::get come a un osservatore rigoroso, e usarlo per gestire la gestione a vita è un segnale rivelatore che c'è qualcosa di sbagliato in corso.

D'altra parte, std::unique_ptr<T> ha release, che è esattamente ciò di cui hai bisogno quando devi intervenire e occuparti della proprietà da solo per un istante. Forse puoi cambiare il tuo design per usare std::unique_ptr<T>? È sempre possibile creare un std::shared_ptr<T> da esso se necessario, eventualmente. (Anche se mentre il std::shared_ptr<T> raccoglie il deleter dal std::unique_ptr<T>, hai ancora bisogno di un trattamento speciale nel caso array come probabilmente si vuole std::shared_ptr<T*> da std::unique_ptr<T[]>.)

+0

Ok, questo è quello che mi sarei aspettato, se non avessi visto quella definizione su cppreference. Inizierò una discussione lì per cambiarlo. – LiKao

Problemi correlati