问题
I'm used to the C++ RAII facilities, and I want to use RAII the right way with managed code in C++/CLI. Herb Sutter and Microsoft both tell me this is the best practice.
I have something like this:
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_ );
}
};
I want (1) automatic resource destruction so I don't have to delete every gcnew'ed object manually, particularly in the face of exceptions; (2) the ability to share objects safely and clearly (passing around std::auto_ptr and the like doesn't qualify); and (3) the ability to have my class consumed by VB or C# and have the cleanup automatically run when the object goes out of scope (e.g., due to an exception).
In standard C++ I'd use std::shared_ptr and std::vector or similar facilities to automate RAII. Here, I could use STL/CLI's vector, but there is no shared_ptr equivalent. The only relevant C++/CLI smart pointer I see is the sparsely documented msclr::auto_handle, which is akin to std::auto_ptr, including transfer-of-ownership semantics, which are not compatible with vectors, though they'd work alright in an array.
What's the proper C++/CLI way to achieve my three goals? (Note also, my main C++/CLI class, Creator in the above, will be consumed by VB/C#.)
[Updates: Added links to Herb Sutter and MS at the top and added goal 3 (consumption by VB/C#).]
回答1:
You can have RAII with managed code: if you have this:
ref class A {
~A() { // implements/overrides the IDisposable::Dispose method
// free managed and unmanaged resources here
}
};
Then you can do this:
void foo()
{
A a(cons_args); // stack-like usage
// use a ...
}
and this will effectively be treated as:
void foo()
{
try
{
A^ a_ = gcnew A(cons_args);
}
finally
{
a_->~A();
}
}
回答2:
I take the correct answer to my question to be Billy ONeal's comment on someone else's answer:
Ah -- you can't do that. There is no way to force the garbage collector to destroy an object. See blogs.msdn.com/b/oldnewthing/archive/2010/08/09/10047586.aspx The right way to do that is to require an explicit close call, and to put a call to that in a finalizer. If for some reason the client code doesn't clean up, the GC will (eventually) clean it up for you (at program termination if nothing else), but you cannot write code that depends on that. – Billy ONeal Sep 10 '10 at 16:02
Besides the link he gives, see also here and here.
回答3:
Not tested, but this should get you started:
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(); }
}
来源:https://stackoverflow.com/questions/3685451/raii-in-c-cli