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#).]
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With