2010-12-31 7 views
5

Qualcuno potrebbe spiegare a un programmatore C++ le differenze più importanti tra i riferimenti Java (e C#) e shared_ptr (da Boost o da C++ 0x).Confronto di shared_ptr con i riferimenti al linguaggio gestito

Sono più o meno consapevole di come shared_ptr è implementato. Sono curioso di conoscere le seguenti differenze:

1) Prestazioni. 2) Ciclismo. shared_ptr può essere ciclato (A e B tengono puntatori l'un l'altro). Il ciclismo è possibile in Java? 3) Qualcos'altro?

Grazie.

risposta

4

Prestazioni: shared_ptr esegue abbastanza bene, ma nella mia esperienza è leggermente meno efficiente di gestione della memoria esplicita, soprattutto perché è di riferimento contati e il conteggio dei riferimenti deve assegnato pure. Quanto bene funzioni dipende da molti fattori e quanto bene si confronta con i garbage collector di Java/C# può essere determinato solo caso per caso (dipende dall'implementazione del linguaggio tra gli altri fattori).

Ciclismo è possibile solo con weak_ptr, non con due shared_ptr s. Java consente di pedalare senza ulteriori indugi; il suo garbage collector sarà break the cycles. La mia ipotesi è che C# faccia lo stesso.

Tutto il resto: l'oggetto indicato da uno shared_ptr viene distrutto non appena l'ultimo riferimento ad esso non rientra nell'ambito. Il distruttore viene chiamato immediatamente. In Java, il finalizzatore non può essere chiamato immediatamente. Non so come C# si comporti su questo punto.

+3

Nota sulle prestazioni, se viene utilizzato 'make_shared' (http://www.boost.org/doc/libs/release/libs/smart_ptr/make_shared.html) allora il conteggio dei riferimenti e l'oggetto di riferimento sono allocati in un blocco . – dalle

+1

Probabilmente, il più grande successo nelle prestazioni con 'shared_ptr' è l'incremento/decremento interbloccato del contatore ref – sbk

+0

@sbk: perché pensi che sia un successo in termini di prestazioni. Solitamente è implementato come una singola istruzione di assemblaggio (non è necessario un blocco su hardware che lo supporti implicitamente). –

3

La differenza chiave è che quando il conteggio dell'utilizzo del puntatore condiviso passa a zero, l'oggetto a cui punta viene distrutto (viene chiamato il distruttore e l'oggetto viene deallocato) immediatamente. In Java e C# la deallocazione dell'oggetto viene posticipata fino a quando Garbage Collector sceglie di deallocare l'oggetto (cioè, non deterministico).

Per quanto riguarda i cicli, non sono sicuro di capire cosa intendi. È abbastanza comune in Java e C# avere due oggetti che contengono campi membri che si riferiscono l'un l'altro, creando così un ciclo. Per esempio una macchina e un motore - l'auto si riferisce al motore attraverso un campo di motore e il motore può fare riferimento alla sua auto attraverso un campo di auto.

+1

Non ci sono "normali" distruttori in C# e Java, hanno solo i finalizzatori, che sono considerati una disfunzione in quelle lingue. I distruttori e la raccolta dei rifiuti sono in disaccordo. GC significa fondamentalmente: nessuna distruzione, o tempo di vita infinito dell'oggetto. –

+0

C'è una differenza tra la nozione di un distruttore, come in una funzione, la chiamata e la distruzione di un oggetto, come nella sua deallocazione. So che questo può spesso ottenere confusione perché la stessa terminologia viene utilizzata per entrambi gli eventi, quindi forse oggetto deallocazione sarebbe una migliore scelta delle parole per fare riferimento alla manifestazione in seguito. –

+4

distruzione non è deallocazione, sono molto diversi concetti e non devono essere usati in modo intercambiabile. –

1

I riferimenti ciclici con puntatori con conteggio di riferimento C++ non verranno eliminati. Puoi usare i puntatori deboli per ovviare a questo problema. Riferimenti ciclici in Java o C# possono essere eliminati, quando il garbage collector si sente come esso.

Quando il conteggio in un puntatore conteggiato con riferimento C++ scende a zero, viene chiamato il distruttore. Quando un oggetto Java non è più raggiungibile, il suo finalizzatore non può essere chiamato tempestivamente o mai. Pertanto, per gli oggetti che richiedono l'eliminazione esplicita di risorse esterne, è necessaria una qualche forma di chiamata esplicita.

1

Prima di tutto, Java/C# ha solo puntatori, non riferimenti, sebbene li chiamino in questo modo. Il riferimento è una caratteristica C++ unica. La raccolta di dati inutili in Java/C# in pratica significa vita infinita. shared_ptr d'altra parte fornisce la condivisione e la distruzione deterministica, quando il conteggio va a zero. Pertanto, è possibile utilizzare shared_ptr per gestire automaticamente qualsiasi risorsa, non solo l'allocazione della memoria. In un certo senso (proprio come qualsiasi progetto RAII) trasforma la semantica dei puntatori in semantica di valore più potente.

+4

La distinzione C++ tra "puntatore" e "riferimento" è piuttosto specifica per quel linguaggio. I riferimenti C#/Java sono da qualche parte tra i due, dato che non puoi fare aritmetica puntatore su di essi. –

+0

@larsmans, Il fatto è che Java, semanticamente, non solo ha dei puntatori, ma ha solo dei puntatori. Manca un meccanismo di riferimento che garantisca che il riferimento non sia mai nullo. Ma in realtà non ha aritmetica puntatore, e la notazione sintattica superficiale non è la solita notazione puntatore. –

+0

ma quello che stai dicendo è fuorviante perché presuppone la definizione C++ di ciò che un "riferimento" è. Java ha riferimenti perché sono chiamati riferimenti. Semplicemente non si comportano come riferimenti C++. – jalf

1

Nessuno ha indicato la possibilità di spostare l'oggetto dal gestore di memoria nella memoria gestita.Quindi in C# non ci sono riferimenti/puntatori semplici, funzionano come gli ID che descrivono l'oggetto che viene restituito dal manager.
In C++ non è possibile ottenere ciò con shared_ptr, perché l'oggetto rimane nella stessa posizione dopo che è stato creato.

Problemi correlati