Access to Disposed Objects

Like the native address-of operator (&), the prefix operator % can be misused. The following code shows an obvious example:

FileStreamA GetFile() {

FileStream fs("sample.txt", FileMode::Open); return %fs;

This function defines a local FileStream variable and returns the tracking handle wrapped by that variable. When that function returns, the local variable leaves its scope, and the FileStream's Dispose method is called. The tracking handle that is returned to the caller refers to an object that has just been disposed.

Accessing an object whose destructor has just been called is obviously not a good idea. This is true for native as well as managed objects. However, access to a destroyed native object typically has more undesired effects than access to a destroyed managed object.

For native objects, destruction and memory deallocation are strictly coupled. For example, when an instance on the native heap is deleted, its destructor is called and the heap can use the object's space for other further allocations. Therefore, reading fields from the deallocated object will likely read random data. Modifying fields from the deallocated object can be even worse, because it can change other objects randomly. This typically causes undefined behavior that is often detected millions of processor instructions later. These scenarios are often difficult to debug, because the source of the problem (an illegal pointer) and the symptoms (undefined behavior because of inconsistent state) often appear unrelated.

Accessing managed objects that have been disposed does not cause access to deallocated memory. The GC is aware of the tracking handle referring to the disposed object. Therefore, it will not reclaim its memory as long as a tracking handle can be used to access the object.

Nevertheless, even with this additional protection level, access to a disposed object is unintended. A caller expects a called object to be alive. To ensure that access to a disposed object is detected, the type System::IO::FileStream (as well as many other disposable reference types) throws an ObjectDisposedException if a method is called on an object that has already been disposed. Throwing a well-defined exception when a disposed object is accessed prevents the possibility of undefined behavior.

For your own classes, you should consider supporting this pattern, too. The following code shows how you can use a simple helper class to protect instances of the FileDumper class against calls after disposal:

ref class DisposedFlag {

bool isDisposed;

StringA objectName; public:

DisposedFlag(StringA objectName)

: isDisposed(false), objectName(objectName)

~DisposedFlag() isDisposed = true;

// support cast to bool operator bool()

return isDisposed;

void EnsureObjectIsNotDisposed()

if (isDisposed) throw gcnew ObjectDisposedException(objectName);

public ref class FileDumper {

FileStream fs; StreamReader sr;

DisposedFlag disposedFlag;


FileDumper(StringA name) : fs(name, FileMode::Open), sr(%fs), disposedFlag("FileDumper")

disposedFlag.EnsureObjectIsNotDisposed(); Console::WriteLine(sr.ReadToEnd());

void CheckDisposed() {

if (disposedFlag)

Console::WriteLine("FileDumper is disposed"); else

Console::WriteLine("FileDumper is not disposed");

In this code, the managed class DisposedFlag wraps a simple Boolean variable. In its constructor, this variable is set to false, and the DisposedFlag destructor sets it to true. Since DisposedFlag is used to define an implicitly dereferenced variable in FileDumper, the DisposedFlag constructor is implicitly called by the FileDumper constructor, and the DisposedFlag destructor is implicitly called by the FileDumper destructor.

To throw an ObjectDisposedException if a call is made after the FileDumper is disposed, Dump simply calls EnsureObjectlsNotDisposed on the implicitly dereferenced DisposedFlag field. EnsureObjectlsNotDisposed simply throws an exception if the wrapped Boolean variable is set to true in the DisposedFlag destructor.

Was this article helpful?

0 0

Post a comment