Smart pointer wrapper for IDisposable in Managed C++
A common idiom I have come to appreciate in C++ is “resource acquisition is initialization”. The idea is that you acquire a resource in a constructor, and dispose of it in the destructor. Using a stack based class, you can make sure your resources are disposed of whether you exit a function normally or through an exception. It leads to easy to read code that eliminates lots of nested error handling and clean up code. It is often used for things like smart pointers (std::auto_ptr) and things that use other resources such as files, where the destructor might close the file. Smart pointers generally redefine the -> operator so that you can deal with them just like regular pointers.
Garbage collected objects are not destroyed as soon as they go out of scope, so this trick does not work. The standard interface IDisposable is recommended as the standard way to add a Dispose() method that is called to clean up a resource. Using the finally block of a try/catch block, you can make sure Dispose is called. Here’s an example in C#:
IDisposable f = new Foo();
try
{
...
}
finally
{
f.Dispose();
}
C# has the “using” statement, which makes this even easier. The above code is equivalent to:
using (f = new Foo())
{
...
}
I really like that the designers of C# added nice features like this. It makes the code much easier to read. Knowing about things like specific interfaces isn’t something that will ever make its way into C++.
In any case, if you are writing Managed C++ and are dealing with an IDisposable, you don’t have the “using” statement available to you, so you end up having to write try/finally code. It’s a bit frustrating to have to deal with longer syntax when C++ supports deterministic destruction. I was working with a coworker on some Managed C++ code today and suggested a smart pointer wrapper for IDisposables that calls Dispose() in the destructor. I found an MSDN article that suggests exactly this. Using their wrapper, you can just write code like:
auto_dispose<Foo> f = new Foo();
You can use f just like a Foo *, and Dispose() will be called when f goes out of scope. I find it kinda cool to use non-garbage collected C++ objects to manage the resources in a garbage collected object.
Comments
- Anonymous
April 01, 2004
Dan, maybe you can explain this to me.
Say I have a custom class. How do I know if I should impliment IDisposable? and if I do impliment it, do people who call me explicitley have to call class.dispose() when they are done with me or does the GC take care of that when the instance goes out of scope?
From what I've read it says to use IDisposable if you need to expose of expensive resources... but what if my class just has a bunch of strings, array lists and so on that it's storing?