2016-01-08 18 views
5

Ho appena scoperto std::shared_ptr s' 'aliasing costruttore' e mi ritrovo a chiedere "perché non std :: unique_ptr hanno un corrispondente?Perché std :: unique_ptr non ha un costruttore aliasing come std :: shared_ptr ha?

Cioè, se si vuole assegnare un Foo modo che si può passare il suo Bar membro per una funzione che dovrebbe gestire tutto il ciclo di vita del Foo, non sarebbe bello essere in grado di farlo?

#include <memory> 

struct B {} 
struct A { 
    B b; 
} 

void f(std::unique_ptr<B> b); 

std::unique_ptr<A> a = std::make_unique<A>(); 
std::unique_ptr<B> b { std::move(a), &(a->b) }; // a now invalid. 
f(std::move(b)); // f now responsible for deleting the A. 

Questo funziona con std :: shared_ptr (http://ideone.com/pDK1bc)

#include <iostream> 
#include <memory> 
#include <string> 

struct B { 
    std::string s; 
}; 
struct A { 
    B b; 
    A(std::string s) : b{s} {}; 
    ~A() { std::cout << "A deleted." << std::endl; } 
}; 

void f(std::shared_ptr<B> b) { 
    std::cout << "in f, b->s = " << b->s << " (use_count=" << b.use_count() << ")" << std::endl; 
} 

int main() { 
    std::shared_ptr<A> a = std::make_shared<A>("hello"); 
    std::shared_ptr<B> b { a, &(a->b) }; 
    a.reset(); // a now invalid. 
    std::cout << "before f, b->s = " << b->s << " (use_count=" << b.use_count() << ")" << std::endl; 
    f(std::move(b)); // f now responsible for deleting the A. 
    std::cout << "after f" << std::endl; 
    return 0; 
} 

uscite del previsto

before f, b->s = hello (use_count=1) 
in f, b->s = hello (use_count=1) 
A deleted. 
after f 

C'è una ragione logica per cui una cosa del genere non è stata inclusa? E/o, è una cattiva idea di emularlo con un unique_ptr<B> con un deleter personalizzato che cancella il A?

risposta

8

Credo che il "problema" sia che, a differenza di std::shared_ptr, il deleter std::unique_ptr non viene cancellato dal tipo. Il deleter predefinito di std::unique_ptr<T> (che ha dimensione zero, codificato nel tipo stesso come parametro di tipo predefinito appena visibile) è semplicemente [](T * p){ delete p; }. Tuttavia, è evidente che unocreato con il numero std::make_unique<B> e uno che è stato creato indicando un membro B di un oggetto A non può avere lo stesso deleter. Il deleter per quest'ultimo caso dovrebbe fare un po 'di aritmetica puntatore per ottenere il puntatore originale A * indietro. Quei due deleters potevano avere lo stesso tipo solo se entrambi memorizzassero un offset o un puntatore interno all'oggetto originale. E questo non avrebbe più dimensioni zero. std::unique_ptr è stato progettato per avere zero overhead rispetto a fare new e delete manualmente, che è una buona cosa. Non vedo alcun inconveniente immediato dall'usare i propri deletatori che memorizzano quel puntatore aggiuntivo, anche se dovrei comunque trovare un caso d'uso in cui troverei ciò utile.

5

shared_ptr ha un overhead di conteggio dei riferimenti. Nel blocco di conteggio di riferimento memorizza anche un deleter esplicito (perché se si sta memorizzando nell'heap, quali sono alcuni altri byte?)

Questo è anche il motivo per cui un shared_ptr a un tipo di base può ricordare di eliminare i tipi derivati ​​senza un agente virtuale.

unique_ptr, d'altra parte, memorizza il suo deleter nell'istanza e il delet predefinito è senza stato - 0 byte utilizzati. Ciò rende l'overhead zero unique_ptr su un puntatore non elaborato in termini di utilizzo della memoria.

Un deleter stateless non può ricordare di cancellare qualcos'altro.

È possibile aggiungere un deleter stateful a unique_ptr che supporta l'aliasing, ma sarà necessario eseguire manualmente l'alias. Uno dei costruttori accetta sia un puntatore che un cancellatore.

+0

E quindi, dal momento che la parte deleter del tipo, non si integra bene con plain 'unique_ptr ' a meno che non si digiti cancella il deleter, che è un ulteriore sovraccarico. –

+0

@ T.C .: Anche se si digita cancella il deleter, non si integra bene con 'unique_ptr >'. Almeno, la conversione è possibile solo in una direzione. –

+0

@ben in teoria in entrambe le direzioni, purché sia ​​consentito il fallimento. – Yakk

Problemi correlati