C++/CLI should be thought of as a language of its own (with a new set of keywords, for example), instead of the C++ superset-oriented Managed C++ (MC++) (whose non-standard keywords were styled like
__value). Because of this, there are some major syntactic changes, especially related to the elimination of ambiguous identifiers and the addition of .NET-specific features.
Many conflicting syntaxes, such as the multiple versions of operator
new() in MC++ have been split: in C++/CLI, .NET reference types are created with the new keyword
gcnew. Also, C++/CLI has introduced the concept of generics (conceptually similar to unmanaged C++ templates, but quite different in their implementation).
Back in MC++, there were two different types of pointers:
__nogc pointers were normal C++ pointers, while
__gc pointers worked on .NET reference types. In C++/CLI the only type of pointer is the normal C++ pointer, and the .NET reference types are accessed through a "handle", with the new syntax
ClassName^ instead of
ClassName*. This new construct is especially helpful when managed and unmanaged code is mixed; it clarifies which objects are under .NET automatic garbage collection and which objects the programmer must remember to explicitly destroy.
A tracking reference in C++/CLI is a handle that is passed by reference rather than by value. They correspond to the "
ref" keyword applied to types in C#, or "
ByRef" in Visual Basic .NET. C++/CLI uses a "
^%" syntax to indicate a tracking reference to a handle. It is similar in concept to using "
*&" (reference to a pointer) in Standard C++.
The following code shows an example use for tracking references. Replacing the tracking reference with a regular handle variable would leave the resulting string array with 10 uninitialized string handles, as only copies of the string handles in the array would be set, due to them being passed by value rather than by reference.
The code above additionally serves as an example in how .NET languages allow users to do slightly different things, as e.g. C# does not allow the user to use
foreach loops in the directly corresponding way. That is;
foreach(ref string s in arr) is illegal in C# as it does not allow
foreach loops to pass values by reference, and other workarounds need to be used in this case.
Another change in C++/CLI is the introduction of the finalizer syntax
!ClassName(), a special type of nondeterministic destructor that is run as a part of the garbage collection routine, so now the destructor syntax
~ClassName() better reflects the "traditional" C++ semantics of deterministic destruction (that is, destructors that can be called by user code). Moreover, the destruction of all managed objects with a defined destructor in a method can be made automatic with the new syntax shown in the example.
In the raw .NET paradigm (for example, direct programming in CIL), the deterministic destruction model is implemented through the
IDisposable interface method
Dispose (which is just what the C++/CLI compiler turns the destructor into), while the nondeterministic one overrides the protected
Finalize method of the root
Usually, deterministic destruction is recommended when non-managed resources are involved or when system-wide limited resources (network/database connections, file streams, etc.) are used. In all other cases, the non-deterministic approach should suffice. Whenever a deterministic destructor is used, the programmer can still benefit from the Garbage Collector, to avoid possible unmanaged memory/resource leaks, by also creating a finalizer (GC-destructor) that just checks whether the destructor has been invoked and calls it if it hasn't (thus adding little performance penalty).
This means that comparing two distinct String references via the operator == will give true whenever the two strings are equal. Of course, operator overloading is static, as is and always should be, not only when writing managed code. Thus, casting to Object ^ will remove the overloading semantics.
The standard semantics would be to only overload by object (the classic C++ way) for native and value types, and to overload only by reference (^) for ref classes. Of course, in C++-only projects, it may be reasonable to decide against overloading by reference, and stick to classic C++ operator overloading semantics also for ref classes, which, as experience tells, will often enough be used with on-the-stack semantics and implemented with a copy constructor and assignment operator.