2010-09-10 2 views
6

Ich bin an die C++ RAII-Funktionen gewöhnt, und ich möchte RAII die richtige Weise mit verwaltetem Code in C++/CLI verwenden. HerbSutter und Microsoft beide sagen mir, das ist die beste Praxis.RAII in C++/CLI

Ich habe so etwas wie dies:

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_); 
    } 
}; 

Ich möchte (1) automatische Ressourcenzerstörung, also muss ich nicht manuell jedes gcnew'ed Objekt löschen, insbesondere angesichts der Ausnahmen; (2) die Fähigkeit, Objekte sicher und klar zu teilen (die Weitergabe von std :: auto_ptr und dergleichen ist nicht zulässig); und (3) die Fähigkeit, dass meine Klasse von VB oder C# konsumiert wird und die Bereinigung automatisch ausgeführt wird, wenn das Objekt den Geltungsbereich verlässt (z. B. aufgrund einer Ausnahme).

In Standard-C++ würde ich std :: shared_ptr und std :: vector oder ähnliche Einrichtungen verwenden, um RAII zu automatisieren. Hier könnte ich den STL/CLI-Vektor verwenden, aber es gibt kein shared_ptr-Äquivalent. Der einzige relevante C++/CLI-Smart-Pointer, den ich sehe, ist der sparsely documented msclr::auto_handle, der std :: auto_ptr ähnelt, einschließlich der Semantik der Eigentumsübertragung, die nicht mit Vektoren kompatibel ist, obwohl sie in einem Array funktionieren würden.

Was ist der richtige C++/CLI-Weg, um meine drei Ziele zu erreichen? (Beachten Sie auch, dass meine Haupt-C++/CLI-Klasse, Creator im obigen, von VB/C# verbraucht wird.)

[Updates: Links zu Herb Sutter und MS an der Spitze hinzugefügt und Ziel 3 hinzugefügt (Verbrauch von VB/C#)]

+0

Genau genommen ist dies nicht RAII, weil Sie Zuweisung, nicht Initialisierung verwenden, um die Ressource zu übernehmen. Es ist ein intelligenter Zeiger, der viel mit RAII teilt. –

+0

Die IDisposable-Schnittstelle ist ein bewährtes Muster in verwaltetem Code. Aber Sie müssen * wirklich * auch einen Finalizer enthalten, damit die Ressource automatisch freigegeben wird. Hinzufügen! Verwaltet(). –

+0

Hans, ich denke du hast deine längere Antwort vielleicht gelöscht. Es war hilfreich und ich wünschte, du hättest es verlassen, damit wir es dort diskutieren könnten. – metal

Antwort

1

ich die richtige Antwort auf meine Frage nehmen Billy ONeal ‚s Kommentar zu jemandem zu sein, sonst Antwort:

Ah - das geht nicht. Es gibt keine Möglichkeit, den Garbage Collector auf zu zwingen, ein Objekt zu zerstören. Siehe blogs.msdn.com/b/oldnewthing/archive/2010/08/09/10047586.aspx Der richtige Weg, dies zu tun, ist ein explizites Schließen zu fordern, und einen Anruf zu diesem in einem Finalizer setzen. Wenn aus irgendeinem Grunde der Client-Code nicht aufzuräumen, wird die GC (schließlich) sauber es für Sie nach oben (bei Programm Kündigung, wenn nichts anderes), aber man kann nicht Code schreiben, dass auf abhängt. - Billy ONeal 10. September '10 bei 16:02

Neben dem Link gibt er, auch here und here.

1

Sie können mit verwaltetem Code haben RAII., wenn Sie dies:

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


Dann können Sie dies tun:

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

und dies wird effektiv behandelt werden:

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

+1, yup, das ist der Weg, es zu tun. Ich mag die Syntax nicht besonders, es ist ein großer Fehlergenerator und gibt den Neulingen sehr wenig Gelegenheit zu erraten, wann sie das Caret benutzen müssen. Aber so sei es. –

+0

+1 - aber ich denke immer noch jemand dies tun muss lesen http://msdn.microsoft.com/en-us/library/ms177197.aspx –

+0

Richtig, aber wie ich im Kommentar in Creators Konstruktor angedeutet, ich muss dynamische Zuordnung statt "GC-Stack" -Zuweisung verwenden. Was ich wirklich brauche, ist ein C++/CLI-Äquivalent zu std :: shared_ptr. – metal

1

Nicht getestet, aber das sollten Sie den Einstieg:

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(); } 
}