C++/CLI: Managed Development with C++

The C++ language supports a number of different programming paradigms—functions and structures support procedural programming, classes, virtual methods, and other related features support object-orientated program, and templates support generic programming. C++/CLI extends the C++ language to allow it to support managed programming, where the classes that are developed are garbage collected and have their lifetime managed by the .NET runtime. The syntax of C++/CLI makes a clear break from standard C++ syntax of object creation and deletion, and makes it very clear that the objects are created and managed by the .NET runtime. In combination with the language changes, the C++ compiler can now generate .NET assemblies that are verifiably type-safe, which makes them easier to deploy in Internet and intranet scenarios.

C++/CLI: The Basics

Visual C++ 2008 supports a number of different types of C++/CLI projects, as shown in Figure 1. Noticeably absent is any type of web projects—the reason behind this is incompatibilities in the C++ and ASP.NET 2 compilation models. Outside of this restriction, Visual C++ gives a wide variety of development options, and it is possible to use business logic contained in a C++/CLI class library in an ASP.NET website written in C# or Visual Basic.NET.

Figure 1: CLR C++ Project Types

The most striking part of C++/CLI is the explicitness of its syntax compared to Managed Extensions for C++. Consider the following code, which allocates a managed string and creates a reference to the managed string from the variable s.

String^ s = gcnew String("123");

There are two obvious deviations from normal C++ syntax: the pointer (*) syntax is replaced by a managed object handle (^), and the new keyword has been replaced by gcnew. Both of these syntactic changes make it very clear to the developer that a managed object is being created and used, and the stricter rules for object handles compared to pointers allow accurate object tracking to occur, which makes garbage collection possible.

To declare a managed type in C++/CLI, the context-sensitive ref keyword is used:

ref class  MyManagedClass{};
ref struct MyManagedStruct{};

The compiler will prevent managed objects from being created on the native heap using by the new operator, and will also prevent native objects from being created on the managed heap using the gcnew operator. Similarly, a compiler error will also be generated if pointer and object handle syntax is used incorrectly.

Even in a managed environment, resource cleanup is still an important issue, and C++/CLI offers a clean, simple syntax to create types that manage resources like database connection handles and window handles that are not managed by garbage collection. C++/CLI uses destructor syntax to implement the Dispose method and uses ! syntax to mark the finalize method of a class:

ref class MyManagedClass{
   ~MyManagedClass(){}     //dispose
   !MyManagedClass() {}    //finalize
};

Dispose methods are called by other code to let an object know that it should clean up the resources it is holding. If the code that is using a managed object forgets to call the Dispose method to free the resources being held, the Finalize method is the back up, and will be called at some later point in time as part of the garbage collection process. The one key difference between the implementation of the Dispose and Finalize methods is that Dispose methods can safely access other managed objects that they reference and also free their resources, whereas a Finalize implementation should deal only with native resources that are held directly by the object. Given the uncertain time delay between the final use of an object and the time that the .NET runtime will execute the Finalize method, explicit object Disposal is preferable.

C++/CLI: Cool Features

One of the most common criticisms of Managed Extensions for C++ was that is didn’t feel like C++. Although it was C++ syntactically, the semantics of how the code worked was very different from the normal native C++ language. One of the most obvious differences was the loss of deterministic resource cleanup. As discussed in the preceding section, the .NET garbage collector will deal with managed memory cleanup, but the multitude of other resource types that require manual cleanup still need the same developer attention and discipline that they do in the native world.

In languages such as C# and Visual Basic.NET, resource cleanup is a significant problem because there is no concept of stack-based allocation, so the problem of calling the Dispose method manually to release resources is left to developers to call for every IDisposable-implementing object, even if the native resources are needed only within the scope of a method. Even though the using statement goes some way to addressing this problem, it still requires that the developer looks up the documentation for every type used and checks whether it implements IDisposable. C++/CLI takes a different approach, and logically separates heap- and stack-based allocation of managed objects in the same way that native C++ physically separates these two concepts. C++/CLI allows both value and reference types to be stack allocated using the same syntax as normal C++, so for the MyManagedClass type shown above, both lines of code below are valid:

MyManagedClass c1;
MyManagedClass^ c2 = gcnew MyManagedClass();

Both lines of code will result in a new instance of the MyManagedClass class being allocated on the managed heap, but the c1 variable is logically a stack-based reference to a MyManagedClass object whereas c2 uses the object handle syntax to reference a heap-based object. The key difference is that the dispose method of c1 (~MyManagedClass()) will be called as soon as c1 goes out of scope, in exactly the same way as it would for a native type. The object that the c2 variable references will behave the same way as a native object, and ~MyManagedClass will not be called until the delete method is called on c2. For experienced C++ developers, having C++/CLI behave exactly as C++ should behave in the critical area of resource management is a huge improvement over other managed language alternatives.

One of the other nice features of C++/CLI is automatic generation of backing store for trivial properties. Properties are a first class .NET concept, and allow access to variables through accessor methods using the same syntax as public member variable access. The key benefit of properties is that they support natural and consistent syntax while allowing the implementation of property accessor methods to change without necessitating a re-compilation of all code the accesses the property. Property get and set methods that merely forward access to an underlying private or protected member variable are tedious to implement, and for this case, C++/CLI can do the tedious work under the hood, allowing a property to be written the same as a member variable with the context-sensitive property keyword before the declaration:

ref class MyManagedClass{
   property int Count;
};

Conclusion

For the experienced C++ developer who wants to produce managed classes, C++/CLI is an excellent language. C++/CLI is designed from the ground up to feel like C++, and although some of the syntax, such as the object handle, will initially appear strange to the eye, the behaviour of C++/CLI code is designed to feel like native C++; this avoids leading the developer unknowingly into coding pitfalls.

C++/CLI contains a number of innovative managed language features like automatic object cleanup and stream-lined property implementation. Building on the lessons learnt in both Managed Extensions for C++ and C#, C++/CLI is a modern and clean extension of C++ into the world of the .NET Framework.

About the Author

Nick Wienholt is an independent Windows and .NET consultant based in Sydney. He is the author of Maximizing .NET Performance and co-author of A Programmer’s Introduction to C# 2.0 from Apress, and specialises in system-level software architecture and development, with a particular focus of performance, security, interoperability, and debugging.

Nick is a keen and active participant in the .NET community. He is the co-founder of the Sydney Deep .NET User group and writes technical articles for Australian Developer Journal, ZDNet, Pinnacle Publishing, Developer.COM, MSDN Magazine (Australia and New Zealand Edition), and the Microsoft Developer Network. An archive of Nick’s SDNUG presentations, articles, and .NET blog is available at www.dotnetperformance.com.

In recognition of his work in the .NET area, Nick was awarded the Microsoft Most Valued Professional Award from 2002 through 2008.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read