Domanda

So che rinnovare qualcosa in un modulo ed eliminarlo in un altro può spesso causare problemi in VC ++. Problemi con diversi runtime. Mischiando moduli con runtime collegati staticamente e / o disallineamenti della versione collegati dinamicamente entrambi possono rovinare tutto se ricordo bene.

Tuttavia, è sicuro utilizzare lo std :: tr1 :: shared_ptr di VC ++ 2008 tra i moduli?

Dal momento che esiste una sola versione del runtime che sa persino cosa sia shared_ptr, il collegamento statico è il mio unico pericolo (per ora ...). Pensavo di aver letto che la versione boost di shared_ptr era sicura da usare in questo modo, ma sto usando la versione di Redmond ...

Sto cercando di evitare di avere una chiamata speciale per liberare oggetti nel modulo di allocazione. (o qualcosa di simile a un " elimina questo " nella classe stessa). Se tutto sembra un po 'confuso, lo sto usando per i test unitari. Se hai mai provato a testare il codice C ++ esistente puoi capire come creativo devi essere a volte. La mia memoria è allocata da un EXE, ma alla fine verrà liberata in una DLL (se il conteggio dei riferimenti funziona nel modo in cui penso che funzioni).

È stato utile?

Soluzione

Liberare la memoria è sicuro, purché provenga dallo stesso contesto gestione della memoria . Hai identificato il problema più comune (diversi runtime C ++); avere mucchi separati è un altro problema meno comune che puoi incontrare.

Un altro problema che non hai menzionato, ma che può essere esasperato da puntatori condivisi, è quando il codice di un oggetto esiste nella DLL e viene creato dalla DLL, ma un altro oggetto esterno alla DLL finisce con un riferimento ad esso (tramite puntatore condiviso). Se quell'oggetto viene distrutto dopo che la DLL è stata scaricata (ad esempio, se è un modulo statico a livello di modulo o se la DLL viene esplicitamente scaricata da FreeLibrary () , il distruttore dell'oggetto condiviso si arresta in modo anomalo.

Questo ti può mordere se tenti di scrivere plugin basati su DLL, liberamente accoppiati. È anche il motivo per cui COM consente alle DLL di decidere quando possono essere scaricate, anziché lasciare che i server COM le richiedano di scaricarle.

Altri suggerimenti

Stai cominciando a vedere quanto sia incredibile shared_ptr :)

Essere al sicuro oltre i confini della DLL è esattamente ciò che shared_ptr è stato progettato per essere (tra le altre cose, ovviamente).

Contrariamente a quanto hanno detto gli altri, non è nemmeno necessario passare un deleter personalizzato durante la costruzione del shared_ptr , poiché l'impostazione predefinita è già simile a

template <typename T>
struct default_deleter {
    void operator()( T * t ) { delete t; }
};

e

shared_ptr<Foo> foo( new Bar );

è equivalente a

shared_ptr<Foo> foo( new Bar, default_deleter<Bar>() );

(cioè non esiste un shared_ptr senza un deleter).

A causa della cancellazione del tipo eseguita sul deleter, il elimina chiamato sempre sarà quello della DLL che ha istanziato il < code> shared_ptr , mai quello della DLL in cui l'ultimo shared_ptr non rientra nell'ambito (vale a dire il shared_ptr che invoca il deleter lo chiamerà tramite un puntatore a una funzione inserita dall'originale shared_ptr ).

Confronta questo con auto_ptr , che incorpora l'operatore delete direttamente nel suo distruttore (inline), il che significa che elimina della DLL che distrugge viene utilizzato il auto_ptr , creando gli stessi problemi dell'eliminazione di un puntatore nudo.

Con la stessa tecnica, le classi polimorfiche che sono sempre contenute in shared_ptr non hanno nemmeno bisogno di un distruttore virtuale, perché il deleter chiamerà sempre il distruttore giusto, anche quando l'ultimo shared_ptr per uscire dall'ambito è un'istanza per la classe base.

Se sei preoccupato, usa la forma del costruttore shared_ptr che accetta un argomento deleter. Il deleter può richiamare nel modulo che ha allocato l'oggetto in modo che l'eliminazione avvenga nel contesto appropriato.

La documentazione di Boost afferma che è compatibile al 100% con TR1, quindi spero che non ci sia nulla di fuorviante al riguardo:

http://www.boost.org /doc/libs/1_37_0/libs/smart_ptr/shared_ptr.htm#constructors

Immagino sia sicuro come usare una qualsiasi delle classi in std tra i moduli.

Cioè: dovrebbe essere sicuro se i moduli usano esattamente la stessa libreria di runtime e esattamente le stesse opzioni e opzioni del compilatore.

Non usare mai la libreria statica di runtime, poiché ogni modulo otterrà la propria istanza di tutti i globi al suo interno.

Il miglior consiglio che ho visto sull'argomento generale è che la memoria dovrebbe essere allocata nello stesso contesto in cui è allocata. Ciò non impedisce a una libreria di restituire un puntatore che il codice dell'applicazione dovrebbe comunque liberare, quindi direi che probabilmente sarai sicuro di passare il shared_ptr in questo modo, poiché è la stessa situazione generale.

Se la semantica del tuo sistema significa che il puntatore viene effettivamente trasferito (nel senso della proprietà) dal tuo exe alla tua dll, allora un auto_ptr potrebbe essere una soluzione migliore. Se, tuttavia, il tuo puntatore è veramente condiviso, probabilmente shared_ptr è la soluzione migliore.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top