C++C# was a brand new language created just for the .NET Framework. The .NET-based version of VB was essentially the same thing, although its name and syntactic style were borrowed from VB 6. But C++ existed well before .NET, and it's been in wide use for many years. Given this, Microsoft decided that while providing some way to create CLR-based software in C++ was essential, so was maintaining compatibility with the existing language. Unlike VB, Microsoft believed that making everyone move to a purely CLR-based version of C++ wasn't a good idea. Accordingly, Visual Studio 2005, like its predecessors, still supports standard C++.
Yet mapping C++ to the CLR presented some challenges. Most important, the original semantics of C++ don't exactly match those of the CLR. They have much in commonboth are object-oriented, for examplebut there are also many differences. C++, for instance, supports multiple inheritance, the ability of a class to inherit simultaneously from two or more parent classes, while the CLR does not.
VB 6 also differed substantially from the CLR, but Microsoft owned VB. The company was free to change it as they wished, so VB's .NET-based incarnation was designed to match the CLR. Microsoft does not own C++, however. Unilaterally changing the language itself to match the CLR would have met with howls of protest. Yet providing no way to create .NET Frameworkbased applications in C++ would also have left many developers unhappy. What's the solution?
The original answer Microsoft chose was to create a set of extensions to the base C++ language. Officially known as Managed Extensions for C++, the resulting dialect is commonly referred to as just Managed C++. C++ is not simple to begin with, and Managed C++ added new complexities. Despite this, Managed C++ was used by a number of organizations for creating .NET applications.
With the 2005 release of the Visual Studio, Microsoft has provided another way to create managed code in C++. While the original managed extensions are still supported, they're now deprecated. Instead, the C++ language itself has been modified, adding new keywords and more for creating applications that run on the CLR. Expressly designed for creating managed code, this language dialect is known as C++/CLI. These extensions are following the standardization path originally defined for the CLI, with the goal of potentially making the C++/CLI dialect available in non-Microsoft environments. This section provides a brief introduction to both C++/CLI and the Managed C++ dialect it replaces.
C++/CLIBefore looking at a C++/CLI example, it's useful to briefly describe some of the extensions made to the language. To make writing CLR-based code as natural as possible, Microsoft chose to add several keywords to the language. To avoid breaking existing C++ code, these keywords use two interesting approaches:
With these ideas in mind, we can now make some sense out of an example. A C++/CLI ExampleHere's the same simple program shown earlier in C# and VB, this time expressed in C++/CLI. As before, the semantics are essentially the same. What's changed is the syntax in which those semantics are expressed. // A C++/CLI example #include "stdafx.h" interface class IMath { int Factorial(int f); double SquareRoot(double s); }; ref class Compute : public IMath { public: virtual int Factorial(int f) { int i; int result = 1; for (i=2; i<=f; i++) result = result * i; return result; }; public: virtual double SquareRoot(double s) { return System::Math::Sqrt(s); } }; void main(void) { Compute ^c = gcnew Compute; int v; v = 5; System::Console::WriteLine( "{0} factorial: {1}", v, c->Factorial(v)); System::Console::WriteLine( "Square root of {0}: {1:f4}", v, c->SquareRoot(v)); } The first thing to notice is how much this example resembles the C# version. Most of the basic syntax and many of the operators are the same. Yet it's different, too, beginning with the #include statement necessary for creating managed code in C++. Following these, the interface IMath is defined, just as before. This time, however, it uses the spaced keyword interface class, described above. The result is a C++ incarnation of a CTS-defined interface.
Next comes the class Compute, which implements the IMath interface. This class is prefaced with C++/CLI's ref class keyword, which indicates that this is a CTS reference class, one whose lifetime is managed by the CLR using garbage collection. The class itself varies a bit in syntax from the C# example, since C++ doesn't express things in exactly the same way, but it's nonetheless very similar.
The example ends with a standard C++ main function. Just as in the earlier examples, it creates an instance of the Compute class and then calls its two methods, all using standard C++ syntax. The most visible difference from those earlier examples (and from standard C++) is the use of the gcnew keyword. This keyword indicates that an instance of a CTS class (i.e., a garbage-collected class, hence the "gc" in "gcnew") is being created. In other words, the Compute class is being created on the heap managed by the CLR rather than on the native heap maintained by C++. (Objects instantiated using the standard C++ new operator are created on this native heap, just as always.)
Another difference is the appearance of the ^ symbol, commonly called a caret or, more informally, a hat, in the declaration of the Compute class. Standard C++ uses the traditional asterisk to indicate a reference. To make immediately clear that a CTS reference type is involved, however, C++/CLI introduces the idea of a handle. Identified by this new symbol, a handle like the one declared here can sometimes be used in ways similar to an ordinary C++ pointer, as shown by the calls to Factorial and SquareRoot later in the program. Because it's actually a reference to a garbage-collected object on the CLR-managed heap, however, it is in fact quite different from an ordinary C++ pointer. This new syntax makes that difference very visible to developers. And just as you'd expect, the output of this example is the same as before: the factorial and square root of five.
C++/CLI TypesC++/CLI allows full access to the .NET Framework, including the types defined by the CLR and more. It's important to note that managed and unmanaged code, classes defined with and without ref, can be defined in the same file, and they can exist in the same running process. Only the managed classes are subject to garbage collection, however; unmanaged classes must be explicitly freed as usual in C++. Table 3-3 shows some of the major CTS types and their equivalents in C++/CLI.
Other C++/CLI FeaturesBecause it fully supports the CLR, there's much more in C++/CLI. Properties can be created using the property keyword, for example, while delegates can be created using the delegate keyword. C++/CLI supports both CLR-defined generics and their cousins, standard C++ templates. Namespaces can be referenced with a using namespace statement, such as using namespace System; Exceptions can be handled using try/catch blocks, and custom CLR exceptions that inherit from System::Exception can be created. Attributes can also be embedded in code using a syntax much like that used in C#.
Except for C++, all languages in Visual Studio compile only to MSIL, and they require the .NET Framework to run. Since all C++/CLI classes are compiled to MSIL, the language can obviously be used to generate Framework-based code. Yet C++ is unique among Microsoft's .NET-based languages in that it also allows compiling directly to a machine-specific binary. For building Windows applications that don't require the CLR, this makes C++ the only way to go.
Managed C++Visual Studio .NET, Microsoft's original tool for creating .NET applications, introduced Managed C++ for creating CLR-based software in C++. With the release of Visual Studio 2005, the use of Managed C++ is now discouraged. Still, plenty of people wrote (or more often extended) C++ applications using this original attempt at combining C++ and the CLR. Given this, it's worth taking a quick look at this now-deprecated dialect. It's also interesting to compare it with its successor, C++/CLI.
Before looking at a Managed C++ example, though, it's useful to describe some of the extensions made to the language. Like C++/CLI, several keywords were added to allow access to CLR services. These keywords all begin with two underscores, following the convention defined in the ANSI standard for C++ extensions. Among the most important of these are the following:
As before, an example is now in order. A Managed C++ ExampleHere's this chapter's standard example, this time in Managed C++: // A Managed C++ example #include "stdafx.h" #using <mscorlib.dll> __gc __interface IMath { int Factorial(int f); double SquareRoot(double s); }; __gc class Compute : public IMath { public: int Factorial(int f) { int i; int result = 1; for (i=2; i<=f; i++) result = result * i; return result; }; public: double SquareRoot(double s) { return System::Math::Sqrt(s); } }; void main(void) { Compute *c = new Compute; int v; v = 5; System::Console::WriteLine( "{0} factorial: {1}", box(v), __box(c->Factorial(v))); System::Console::WriteLine( "Square root of {0}: {1:f4}", box(v), __box(c->SquareRoot(v))); } Not too surprisingly, this example looks similar to the versions shown earlier in C# and C++/CLI. The differences are interesting, however, and they start with the #include and #using statements necessary for creating Managed C++ code. The interface IMath is once again defined, but this time, it uses the __interface keyword and precedes it with the __gc keyword. The combination has the same meaning as interface class in C++/CLI. The Compute class is also declared with the __gc keyword, expressing in a different way what C++/CLI says with ref.
The example ends with a standard C++ main function. Just as before, it creates an instance of the Compute class and then calls its two methods, all using standard C++ syntax. The only substantive difference is in the calls to WriteLine. Because this method expects reference parameters, the __box operator must be used to pass the numeric parameters correctly. Boxing also occurred for this parameter in C# and VB, but it was done automatically. Because C++ was not originally built for the CLR, however, a Managed C++ developer must explicitly request this operation.
Managed C++ TypesLike C++/CLI, Managed C++ allows full access to the .NET Framework, lets managed and unmanaged code be defined in the same file, and more. To a very large degree, the two C++ dialects just provide different ways of expressing identical semantics. Table 3-4 shows some of the major CLR types and their equivalents in Managed C++.
Other Managed C++ FeaturesLike C++/CLI, Managed C++ provides full access to the CLR. Delegates can be created using the __delegate keyword, namespaces can be referenced with using namespace, just as in C++/CLI, exceptions and attributes can be used, and more. Managed C++ isn't a weak tool, and its use wasn't deprecated because it lacked power. Rather, the people at Microsoft who control this technology felt that their first attempt at mapping C++ to the CLR wasn't good enough, and so going forward, new C++ managed code should be created using C++/CLI instead.
|