Ci sono stati una serie di domande oggi per quanto riguarda std::weak_ptr
e std::owner_less
e il loro utilizzo nei contenitori associativi std::set
e std::map
. Esistono numerosi post che affermano che l'utilizzo di weak_ptr
in un std::set
non è corretto, poiché se il puntatore debole scade, sarà il comportamento non definito. È corretto?E 'sicuro usare un weak_ptr in uno std :: set o la chiave di std :: map
risposta
Uno dei motivi std::owner_less
è fornire questo ordine e garantire la sua sicurezza in presenza di punti deboli in scadenza. Mia logica è
primo luogo, la definizione di std::owner_less
operatore() definisce un ordinamento debole rigorosa definita 25.4
sotto la relazione di equivalenza definita da
operator()
,!operator()(a, b) && !operator()(b, a)
, dueshared_ptr
oppure le istanzeweak_ptr
sono equivalenti se e solo se condividono la proprietà o sono entrambe vuote.
I due casi sono
- Essi condividono lo stesso oggetto, che in pratica significa che condividono lo stesso oggetto conteggio di riferimento.
- Sono entrambi vuoti.
Ora, credo che la confusione sia finita nel secondo periodo. La chiave è che "vuoto" nello standard significa che lo weak_ptr
non condivide la proprietà con alcun oggetto. Ancora una volta, gli stati standard
constexpr weak_ptr() noexcept;
Effetti: costruisce un
weak_ptr
oggetto vuoto.
Postconditions:use_count() == 0
.weak_ptr(const weak_ptr& r) noexcept;
template<class Y> weak_ptr(const weak_ptr<Y>& r) noexcept;
template<class Y> weak_ptr(const shared_ptr<Y>& r) noexcept;
Richiede: Il secondo e terzo costruttori non devono partecipare alla risoluzione di sovraccarico a meno
Y*
è implicitamente convertibile inT*
.Effetti: Se
r
è vuoto, costruisce un oggetto vuotoweak_ptr
; altrimenti, costruisce un oggettoweak_ptr
che condivide la proprietà conr
e memorizza una copia del puntatore memorizzata inr
.Postcondizioni:
use_count() == r.use_count()
.
Swap è definito come scambiare gli stati dei due weak_ptr
s, e assegnazione viene definita usando i costruttori sopra insieme ad uno scambio.
Essi chiave da notare qui è che l'unico modo per creare un vuoto weak_ptr
è per difetto costruirlo, o copiare/spostare/assegnare una dall'precedentemente vuoto weak_ptr
o shared_ptr
. È anche importante notare che non è possibile ottenere uno weak_ptr
vuoto semplicemente lasciando scadere il weak_ptr
. Uno scaduto weak_ptr
ha semplicemente uno use_count
di zero.
In pratica, quando viene creato un shared_ptr
, un oggetto conteggio di riferimento deve essere creato, sia separato dai dati utilizzando il costruttore shared_ptr
, o nella stessa allocazione di memoria quando si utilizza std::make_shared
. Quando un weak_ptr
viene creato da quello shared_ptr
, punta a quella stessa struttura di controllo e numero di riferimento. Quando lo shared_ptr
viene distrutto, può distruggere i dati, ma l'oggetto conteggio di riferimento deve rimanere finché tutti i weak_ptr
che condividono la proprietà vengono rimossi. In caso contrario, weak_ptr
avrà un riferimento di puntatore pendente.
Quindi, tutto questo nel loro insieme significa che è sicuro da usare std::weak_ptr
in quanto chiave di un std::map
o in un std::set
, fino a quando si utilizza std::owner_less
per eseguire l'ordine. Quanto sopra garantisce che l'ordine di weak_ptr
rimarrà lo stesso anche se scade mentre è nel contenitore.
Puoi aggiungere una conclusione a questa risposta? :) – Drax
@Drax: certo. L'ho scritto a tarda notte e ho perso alcuni piccoli dettagli del genere. –
Bello, grazie :) – Drax