2009-09-10 16 views
26

boost::shared_ptr ha un costruttore di insolitoPer cosa è usato shared_ptr di boost (shared_ptr <Y> const & r, T * p)?

template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p); 

e io sono un po 'perplesso su ciò che questo sarebbe utile per. Fondamentalmente condivide la proprietà con r, ma .get() restituirà p. nonr.get()!

Ciò significa che si può fare qualcosa di simile:

int main() { 
    boost::shared_ptr<int> x(new int); 
    boost::shared_ptr<int> y(x, new int); 

    std::cout << x.get() << std::endl; 
    std::cout << y.get() << std::endl; 

    std::cout << x.use_count() << std::endl; 
    std::cout << y.use_count() << std::endl; 
} 

E otterrete questo:

0x8c66008 
0x8c66030 
2 
2 

Nota che i puntatori sono separati, ma entrambi affermano di avere un use_count di 2 (dal momento che condividono la proprietà dello stesso oggetto).

Così, il int di proprietà di x esisterà finché xoy è intorno. E se capisco che i documenti sono corretti, il secondo int non viene mai distrutto. Ho confermato questo con il seguente programma di test:

struct T { 
    T() { std::cout << "T()" << std::endl; } 
    ~T() { std::cout << "~T()" << std::endl; } 
}; 

int main() { 
    boost::shared_ptr<T> x(new T); 
    boost::shared_ptr<T> y(x, new T); 

    std::cout << x.get() << std::endl; 
    std::cout << y.get() << std::endl; 

    std::cout << x.use_count() << std::endl; 
    std::cout << y.use_count() << std::endl; 
} 

Questo uscite (come previsto):

T() 
T() 
0x96c2008 
0x96c2030 
2 
2 
~T() 

Quindi ... qual è l'utilità di questa insolita costrutto che condivide la proprietà di un puntatore , ma funge da come un altro puntatore (che non possiede) quando viene utilizzato.

+7

Buona domanda. +1 – GManNickG

+3

TL; Versione DR: crea un puntatore a un sottooggetto di 'r'. –

risposta

29

E 'utile quando si desidera condividere un membro della classe e un'istanza della classe è già uno shared_ptr, come il seguente:

struct A 
{ 
    int *B; // managed inside A 
}; 

shared_ptr<A> a(new A); 
shared_ptr<int> b(a, a->B); 

condividono il conteggio dell'utilizzo e roba. È l'ottimizzazione per l'utilizzo della memoria.

+1

una buona risposta. chiaramente, in questo esempio, vorremmo mantenere l'oggetto di 'a' in giro fintanto che' b' è in giro. Penso che abbiamo un vincitore. –

+0

Non solo un'ottimizzazione per l'utilizzo della memoria, ma nell'esempio specifico, l'utilizzo di un approccio diverso finirebbe con una chiamata a 'delete (a-> B)' che potrebbe essere inaspettata (si consideri 'struct A {int b;}; shared_ptr a (nuovo A); shared_ptr b (a, & a-> b) ') –

2

Potrebbe essere presente un puntatore a un driver o una struttura di dati di un livello inferiore della API che potrebbe allocare dati aggiuntivi tramite la sua API di livello inferiore o altri mezzi. In questo caso potrebbe essere interessante aumentare il valore_uso ma restituire i dati aggiuntivi se il primo puntatore possiede gli altri puntatori dati.

8

Per espandere su leiz's e piotr's risposte, questa descrizione di shared_ptr<> 'aliasing' è da una carta WG21, "Improving shared_ptr for C++0x, Revision 2":

III. Aliasing Supporto

Gli utenti esperti spesso richiedono la capacità per creare un'istanza pshared_ptr che condivide la proprietà con un'altra (master) shared_ptrq ma punti per un oggetto che non è una base di *q. *p può essere un membro o un elemento di *q, ad esempio.Questa sezione propone un ulteriore costruttore che può essere utilizzato per questo scopo .

Un effetto collaterale interessante di questo aumento di potenza espressiva è che adesso i *_pointer_cast funzioni possono essere implementate in codice utente. La funzione di fabbrica make_shared presentato avanti in questo documento può anche essere implementato utilizzando solo il pubblico interfaccia shared_ptr tramite il costruttore aliasing.

Impact:

Questa caratteristica estende l'interfaccia di shared_ptr in modo compatibile con le versioni che ne aumenta la potenza espressiva ed è quindi fortemente raccomandato da aggiungere al C++ 0x norma . Non introduce problemi di compatibilità binario di origine e .

testo proposto:

Aggiungi shared_ptr [util.smartptr.shared] la seguente costruttore:

template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p); 

Aggiungere il seguente alla [util.smartptr. shared.const]:

template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p); 

Effetti: Costruisce un'istanza shared_ptr che memorizza p e condivide la proprietà conr.

Postcondizioni:get() == p && use_count() == r.use_count().

Numero: niente.

[Nota: Per evitare la possibilità di un puntatore penzoloni, l'utente di questo costruttore deve garantire che p rimane valido almeno fino gruppo di proprietà di r è distrutta. --end nota]

[Nota:. Questo costruttore consente la creazione di un vuoto shared_ptr esempio con un puntatore non NULL memorizzato. --end nota.]

4

È inoltre possibile utilizzare questo per mantenere i puntatori colato dinamici, vale a dire:

class A {}; 
class B: public A {}; 

shared_ptr<A> a(new B); 
shared_ptr<B> b(a, dynamic_cast<B*>(a.get())); 
+0

un altro uso interessante che rientra chiaramente nella categoria di "aliasing". Buon punto –

0

Per "shared_ptr<B> b(a, dynamic_cast<B*>(a.get()));"

penso che non è il metodo consigliato utilizzando smart pointer.

Il modo consigliato di fare questo tipo di conversione dovrebbe essere:

shared_ptr<B> b(a); 

Poiché nel documento Boost si dice che:

shared_ptr<T> può essere implicitamente convertito shared_ptr<U> quando T * può essere implicitamente convertito in U *. In particolare , shared_ptr<T> è conversione implicita shared_ptr<T> const, a shared_ptr<U> dove U è una base accessibile di T, e shared_ptr<void>.

In aggiunta a ciò, abbiamo anche dynamic_pointer_cast che può fare direttamente la conversione in oggetto puntatore intelligente ed entrambi questi due metodi sarebbe molto più sicuro che la colata manualmente modo puntatore non elaborato.

0

ho messo costruttore aliasing di shared_ptr in uso nella mia piccola biblioteca:

http://code.google.com/p/infectorpp/ (solo il mio semplice contenitore CIO)

Il punto è che da quando ho bisogno di uno shared_ptr di tipo noto per essere tornato da un classe polimorfa (che non conosce il tipo). Non ero in grado di convertire implicitamente la shared_ptr al tipo di cui avevo bisogno.

Nel file "InfectorHelpers.hpp" (riga 72-99) è possibile vedere quello in azione per il tipo IAnyShared.

Aliasing costruttore crea shared_ptr che non elimina i puntatori sono in realtà puntano a, ma ancora aumentare il contatore di riferimento all'oggetto originale e che può essere enormemente utile.

Fondamentalmente è possibile creare un puntatore a qualsiasi cosa utilizzando il costruttore aliasing e minacciarlo come contatore di riferimento.

//my class 
std::shared_ptr<T> ist; 
int a; //dummy variable. I need its adress 

virtual std::shared_ptr<int> getReferenceCounter(){ 
    return std::shared_ptr<int>(ist,&a); //not intended for dereferencing 
} 

virtual void* getPtr(); //return raw pointer to T 

ora abbiamo sia "un contatore di riferimento" e un puntatore ad un'istanza di T, dati sufficienti per creare qualcosa con il costruttore aliasing

std::shared_ptr<T> aPtr(any->getReferenceCounter(), //share same ref counter 
       static_cast<T*>(any->getPtr())); //potentially unsafe cast! 

Non pretendo di aver inventato questo utilizzare per il costruttore aliasing, ma non ho mai visto qualcun altro a fare lo stesso. Se stai indovinando se quel codice sporco funziona, la risposta è sì.

Problemi correlati