NGWS SDK Documentation  

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

Managed Classes

Managed classes are a powerful and versatile feature of Managed Extensions for C++. In addition to the standard features of a C++ class, the NGWS runtime and the Visual C++ compiler provide new features for managed classes:

There are three kinds of managed classes:

Managed class type Purpose
Garbage-Collected Classes Allocated on the NGWS runtime heap and the most general-purpose type of managed class.
Value Classes Represents a small, short-lived data item (allocated on the stack) for which full garbage collection would be too costly.
Managed Interface Classes Offers direct support for COM-style interface programming in C++.

The following features apply to all types of managed classes:

The following restrictions apply to all types of managed classes:

Garbage-Collected Classes

Because garbage-collected classes are allocated from the NGWS runtime heap, the following usage restrictions exist:

Destroying a Managed Object

As in standard C++, a managed class may have a destructor, which can be called explicitly.

Note   During garbage collection, the destructor is invoked before the associated memory is released.

A managed class may be explicitly destructed, either by calling the destructor directly or by using the delete operator. This allows an object to release its resources at a well-defined point. The destructor will not be called again when the garbage collector runs; only the memory will be reclaimed.

The following example demonstrates the explicit invoking of a managed object destructor:

void main ()
{
   ManagedObject* mObj1 = new ManagedObject();
   mObj1->AllocateResources();   // Allocate resources from the managed heap.
   delete mObj1;   // Frees those resources now.

// Alternatively, the objects destructor may be called directly
   mObj1 = new ManagedObject();
   mObj1->~ManagedObject();
}

If a garbage-collected class derives from a garbage-collected class authored in another language, the finalization method (if any) of the base class is treated as the destructor and is called automatically at the end of the destructor for the derived class.

Note   A garbage-collected class may implement a finalization method. This method is called before unloading the object and is automatically called only once for each class.

If you are not certain that an object is dead, you can assign the value 0 to the pointer that references it. If that is the last pointer to the object, the garbage collector will automatically reclaim it.

Value Classes

A value class differs from a garbage-collected class in that instances can be allocated on the run-time stack and as static or heap-allocated variables under certain conditions. A value class is declared by applying the __value keyword to a class or struct declaration. Value classes are designed to hold small data items with short lifetimes, so as not to have the overhead of garbage collection for every allocation. They can also be members of a garbage-collected class.

In addition to those features common to all managed classes, the following features are also supported for value classes:

In addition to those restrictions common to all managed classes, the following restrictions are also in effect for value classes:

Value Classes and Boxing

When the __box keyword is applied to a class or struct object, it is referred to as boxing the class or struct. This instance can then be treated as a managed object.

For every definition of a value class, there exists a unique boxed value class type corresponding to the original value class. All boxed value class types inherit from the garbage-collected class System::ValueType. Enumerated types that are boxed (sometimes referred to as managed enums) inherit from the garbage-collected class System::Enum. You cannot define boxed types directly; the compiler generates them on demand as needed. Referring to the boxed value class by its boxed type is more precise than using the generic type Object and allows the user to avoid expensive dynamic cast operations when accessing the underlying value type.

Because a boxed value class implicitly inherits from System::ValueType and therefore System::Object, a value class may directly call any function it specifically implements without being boxed first. This includes any overrides of virtual member functions defined in System::ValueType. The following example overrides the ToString member of ValueType and then invokes it directly from an instance of the value class V:

__value struct V
{
   // Override ValueType::ToString
   String *ToString() { return i.ToString(); }
   int i;
};

void main()
{
   V v = {10};
   Console::WriteLine(v.ToString());   // Boxing not required
}

A value class member can be accessed from the boxed version using the same syntax as accessing an unboxed value member. The following example demonstrates this by accessing the data member (i)of a boxed value class (V):

__value struct V
{
   int i;
};

void main()
{
   V v = {10};
   __box V* pbV= __box(v);
   assert(pbV->i == 10);
}

To call a virtual function of System::ValueType that has not been overridden in the value type, boxing is required. The following example uses the same value class (without the explicit override of ToString), invoking ToString by boxing the value class first:

__value struct V
{
   int i;
};

void main()
{
   V v = {10};  
   v.i = 10;
   Console::WriteLine(v.ToString());   // Error: boxing required
   Console::WriteLine(__box(v)->ToString());   // OK: prints V
}

Managed Interfaces

An interface declaration is made by applying the __interface keyword to an existing class declaration:

__interface IMyInterface
{
   void method_one();
};

A managed interface embodies the COM notion of an interface (with the added benefits of being managed) and is declared by applying the __gc keyword to an existing interface declaration:

__gc __interface IMyInterface
{
   void method_one();
};

The previous code sample declares a managed interface (IMyInterface) that is essentially a managed C++ abstract base class. Like an abstract base class, the methods of a managed interface are implicitly pure virtual methods.

The following rules apply to managed interfaces:

Implementation of Ambiguous Base Interface Methods

Managed Extensions for C++ allow two or more base interfaces of a class to declare identical methods. These ambiguous interface methods are implemented by providing a fully qualified name in the definition. The following example demonstrates this by declaring and defining the Calculate method for both sample interfaces IAccount and IStatement:

__gc __interface IAccount
{
   void Calculate();
};

__gc __interface IStatement
{
   void Calculate();
};

__gc struct CMyStatement: IAccount, IStatement
{
   void IAccount:: Calculate () {};   // OK
   void IStatement:: Calculate () {};   // OK
};

Default Implementation of a Method

Managed Extensions for C++ automatically provide a default implementation for methods that are not defined by the child class. Default implementation can also used to implement ambiguous base interface methods.

For example, the following code sample declares a class (CMyBase) and a managed interface (IMyInterface). Another class (CMyChild) is derived from CMyBase and IMyInterface but does not provide an implementation for the derived method Calculate:

__gc __interface IMyInterface
{
   void Calculate();
};
__gc struct CMyBase
{
   void Calculate(){}
};
__gc struct CMyChild : CMyBase, IMyInterface
{
   // By default, CMyChild uses CMyBase::Calculate to implement
   // IMyInterface::Calculate
};
void main()
{
   IMyInterface* pI = new CMyChild;   // OK: CMyChild is not abstract
   pI->Calculate();   // ok: calls CMyChild::Calculate (via default
                      //implementation)
}

See Also

Introduction to Managed Extensions for C++