2010-09-10 12 views
6

Estoy acostumbrado a las instalaciones de C++ RAII, y quiero usar RAII de la manera correcta con el código administrado en C++/CLI. HerbSutter y Microsoft me dicen que esta es la mejor práctica.RAII en C++/CLI

tengo algo como esto:

ref struct Managed 
{ 
    // No default constructor 
    Managed(/*...*/) { /*...*/ } 
    ~Managed() { /* Important non-managed resource release here */ } 
    // ... 
}; 

ref struct UsesManaged 
{ 
    Managed^   m_; 
    array<Managed^>^ a_; 

    UsesManaged(Managed^ m, array<Managed^>^ a) : m_(m), a_(a) {} 
    // ... 
}; 

ref struct Creator 
{ 
    Managed^   m_; 
    array<Managed^>^ a_; 
    UsesManaged^  u_; 

    Creator() 
    { 
     // Must allocate dynamically here, not in initializer list 
     // because in my real code, I use "this" here for a callback. 
     m_  = gcnew Managed(/*...*/); 
     a_  = gcnew array<Managed^>(2); 
     a_[ 0 ] = gcnew Managed(/*...*/); 
     a_[ 1 ] = gcnew Managed(/*...*/); 
     u_  = gcnew UsesManaged(m_, a_); 
    } 
}; 

quiero (1) la destrucción automática de recursos, así que no tengo que eliminar todos los objetos gcnew'ed manualmente, sobre todo en la cara de excepciones; (2) la capacidad de compartir objetos de forma segura y clara (pasar por std :: auto_ptr y similares no califica); y (3) la posibilidad de que mi clase sea consumida por VB o C# y haga que la limpieza se ejecute automáticamente cuando el objeto se salga del alcance (por ejemplo, debido a una excepción).

En C++ estándar usaría std :: shared_ptr y std :: vector o instalaciones similares para automatizar RAII. Aquí, podría usar el vector de STL/CLI, pero no existe el equivalente de shared_ptr. El único puntero inteligente C++/CLI relevante que veo es sparsely documented msclr::auto_handle, que es similar a std :: auto_ptr, incluida la semántica de transferencia de propiedad, que no son compatibles con vectores, aunque funcionarían correctamente en una matriz.

¿Cuál es la forma correcta de C++/CLI para alcanzar mis tres objetivos? (Nota también, mi clase principal de C++/CLI, Creador en el anterior, será consumida por VB/C#.)

[Actualizaciones: Se agregaron enlaces a Herb Sutter y MS en la parte superior y se agregó el objetivo 3 (consumo de VB)/C#)]

+0

Estrictamente hablando, esto no es RAII porque está utilizando la asignación, no la inicialización, para tomar posesión del recurso. Es un puntero inteligente que comparte mucho con RAII. –

+0

La interfaz IDisposable es un patrón bien establecido en el código administrado. Pero * realmente * también tiene que incluir un finalizador, por lo que la liberación del recurso se realiza automáticamente. Añadir! Administrado(). –

+0

Hans, creo que es posible que haya eliminado su respuesta más larga al respecto. Fue útil, y desearía que lo hubieras dejado para poder discutirlo allí. – metal

Respuesta

1

Tomo la respuesta correcta a la pregunta de ser comentario Billy ONeal 's de alguien la respuesta de otro:

Ah, no puedes hacer eso. No hay forma de para forzar al recolector de basura a destruir un objeto. Consulte blogs.msdn.com/b/oldnewthing/archive/2010/08/09/10047586.aspx La manera correcta de hacerlo es requiriendo una llamada cercana explícita, y poner una llamada en un finalizador. Si por alguna razón el código de cliente no a limpiar, el GC (con el tiempo) limpia para arriba para usted (en el programa de terminación si no otra cosa), pero no puede escribir código que depende de que . - Billy ONeal Sep 10 '10 en 16:02

Además del enlace que da, véase también here y here.

1

puede tener RAII con código administrado:. si tiene esto:

ref class A { 
    ~A() { // implements/overrides the IDisposable::Dispose method 
     // free managed and unmanaged resources here 
    } 
}; 


entonces usted puede hacer esto:

void foo() 
{ 
    A a(cons_args); // stack-like usage 
    // use a ... 
} 

y esto va a ser efectivamente tratada como:

void foo() 
{ 
    try 
    { 
    A^ a_ = gcnew A(cons_args); 
    } 
    finally 
    { 
    a_->~A(); 
    } 
} 
+0

+1, sí, esta es la manera de hacerlo. No me gusta mucho la sintaxis, es un gran generador de errores y les da a los novatos muy pocas posibilidades de adivinar cuando tienen que usar el cursor. Pero que así sea. –

+0

+1 - pero sigo pensando que cualquiera que haga esto necesita leer http://msdn.microsoft.com/en-us/library/ms177197.aspx –

+0

Correcto, pero como insinué en el comentario en el constructor del Creador, tiene que usar la asignación dinámica en lugar de la asignación de "pila GC". Lo que realmente necesito es un C++/CLI equivalente a std :: shared_ptr. – metal

1

no han sido evaluados, pero esto debe empezar:

template<typename T> 
value class counted_handle 
{ 
    ref struct Count { int refCount; Count() : refCount(1) {} }; 
    T^ m_sharedHandle; 
    Count^ m_sharedCount; 

    void release() { if (m_sharedCount && 0 == --sharedCount->refCount) delete m_sharedHandle; m_sharedCount = nullptr; m_sharedHandle = nullptr; } 
    void addref(if (m_sharedCount) ++m_sharedCount->refCount; } 
public: 
    counted_handle() : m_sharedHandle(nullptr), m_sharedCount(nullptr) {} 
    counted_handle(T^ handle) : m_sharedHandle(handle), m_sharedCount(gcnew Count()) {} 
    counted_handle(counted_handle<T>% src) : m_sharedHandle(src.m_sharedHandle), m_sharedCount(src.sharedCount) { addref(); } 
    void ~counted_handle() { release(); } 
    counted_handle<T>% operator=(counted_handle<T>% src) { src.addref(); release(); m_sharedHandle = src.m_sharedHandle; m_sharedCount = src.m_sharedCount; } 
    counted_handle<T>% operator=(T^ handle) { release(); m_sharedHandle = handle; m_sharedCount = gcnew Count(); } 
} 
Cuestiones relacionadas