From C ++ to C ++/CLI

Source: Internet
Author: User
Tags finally block

Liu weipeng (pongba)/Wen

It seems that only one "/CLI" is written after C ++, but its meaning is far more than that, Google's c ++. for this reason, the moderated version has also started a discussion for several weeks. Most people in China are not familiar with C ++/CLI, and Google is already smoking around...
Just as we make any other choice, the most important thing before selecting is to know why C ++/CLI offers advantages? Why should we (Standard C ++ programmers) Choose C ++/CLI instead of C #? What can we get? Does the CLI platform constrain the capabilities of C ++?
These are all questions from the Standard C ++ community. From the discussion above on Google, more programmers from the Standard C ++ community worry about whether C ++/CLI will constrain the capability of Standard C ++, some people may also be skeptical about the C ++/CLI capabilities to change the Standard C ++ development direction. Others ask what C ++/CLI can bring.
These raised questions are answered one by one on Google. The good news is that the situation may be better than optimistic people think --
Has the world changed?
For programmers who are familiar with the standard C ++, is it the world they are familiar with in C ++/CLI? In the standard C ++ world, the various magic bars in their hands-operator overload | template | multi-inheritance (language), STL | boost | ACE (Library) -- Can you still wave a colorful flame? Isn't the shadow of managed C ++ extension still shrouded in the Standard C ++ in the. NET environment, just like a tiger with a tooth removed?
The answer is: what you can do in the past, and what you can do now, the world just becomes wider --
What is C ++/CLI?
L c ++/CLI is a set of standardized language extensions (Standard C ++ extensions), rather than a new language. Therefore, C ++/CLI is a superset of Standard C ++.
L c ++/CLI is an ECMA standard [1] (and will be submitted to the ISO Standardization Committee), rather than Microsoft's proprietary language. Many organizations (or companies) are involved in the revision of C ++/CLI standards, including Edison Design Group and dinkumware, after Microsoft submitted the C ++/CLI standard draft to the ECMA organization, it gave up control of it, but used it as a standard for public development, any user who uses C ++/CLI can give their own suggestions for its development.
L c ++/CLI aims to bring C ++ to the CLI platform so that C ++ can maximize its capabilities on the CLI platform. Instead of putting C ++ constraints on the CLI platform (CLI itself is also ISO standardized. On the contrary, the capabilities of the original Standard C ++ have not been weakened, and, through the standard extension in C ++/CLI, c ++ has no dynamic programming capability and a series of first class.. net. These extensions are not proprietary, but presented in a standard way.
What are the advantages of C ++/CLI?
L Dynamic Programming and refelection-Standard C ++ is a very static language. The principle is to check the program's legitimacy and logic during the compilation period as much as possible. Standard C ++ lacks dynamic information during runtime. For example, only the typeid operator can be used to query dynamic objects in the runtime of Standard C ++, while the typeinfo class returned by typeid () is a unique identifier, but it only stops being "unique". First, the standard C ++ does not specify the underlying binary representation of typeinfo. Therefore, it is impossible to use it as the unique identifier of a cross-platform class, second, the typeinfo class almost only represents the class name, this kind of "thin" runtime type information prevents the capability of Standard C ++ in the distributed field (IDL is designed to make up for the lack of standard C ++ type information during runtime, however, IDL is not necessary for languages with metadata such as Java or C #. At the same time, IDL makes the use of C ++ in the distributed field less convenient ). Due to the native features of the Standard C ++, almost all types of information are lost once the code is compiled, making it impossible to make almost any changes to the program itself during the runtime, in other words, once compiled, the standard C ++ code almost becomes dead. C ++/CLI has changed this situation, and C ++/CLI has complete metadata, allowing the program to query complete type information at runtime, you can also dynamically create objects by type information, or even dynamically create types and add methods. This powerful runtime dynamic feature is required for modern application fields (such as distributed Web applications.
L GC-now no one will say "adding GC to C ++ is the end of C ++. Even Bjarne stroustrup agrees that if C ++ is to be used in large-scale or super-large software development, it is best to have a good optional GC support. GC or not is no longer a question worth arguing about. The problem is, how can we implement it better? GC in C ++/CLI is one of the most powerful GC mechanisms at present-generational garbage collection. The advantage of GC is that it can simplify the development model of software. GC can greatly increase productivity in areas where efficiency is not critical. GC in C ++/CLI is very important: it is optional. This is different from Java or C #. For the latter, GC is everywhere and objects can only be allocated to managed stacks. In C ++/CLI, If you allocate your objects to native heap, you will get the same efficient memory management as standard C ++. If you allocate an object to the managed heap, the memory of the object will be automatically reclaimed by GC. This hybrid memory management environment is related to the positioning of C ++/CLI. After all, the positioning of C ++/CLI is. system-level programming languages on the. NET platform, so efficiency and underlying control are very important, so native heap is retained. Later you will see the advantages of this programming environment.
L BCL--.NET platform on the basic class library, the rich class in the Bcl greatly facilitates developers. C ++/CLI can fully use any class in BCl.
L portability-there is no doubt that portability is a critical issue, especially for people in the Standard C ++ community. C ++/CLI answers this question: "If your code does not depend on the local binary library, you can" compile it once and run it everywhere (in. NET platform) (use the/CLR: Pure compilation option to compile the code into portable pure msil code ). If some part of your code depends on a Local Binary Library (such as a C input/output stream Library), these parts are still source code portable, while other parts can be "compiled once, run anywhere ". For standard C ++, only source code portability has always been guaranteed, so we have not lost anything. On the contrary, if we comply with the Protocol-we do not need a Local Binary library, for example, replace the C input and output stream library with the input and output stream library in the Bcl-you can get the promise of "one compilation, run anywhere", that is, your code is compiled (/CLR: pure) can be in any other. NET platform -- UNIX, Mono under Linux (transplanted to Unix, Linux.. net), and the rotor (. net open source project), and so on.
Programmers who get used to the Standard C ++ input/output stream may complain-why should we use the output stream in Bcl? The standard iostream is ready! There is actually a solution here. The reason why iostream Code cannot be "compiled once and run everywhere" is that the Code depends on the local binary lib file, if you can re-compile the implementation of iostream into pure msil code, then the code that uses it can be fully run everywhere after compilation. Currently, this is a solution to be discussed. However, at least when faced with the situation that "it depends on some platform-related binary code, you can encapsulate the platform-related code into a DLL file-compile different binary versions for each target platform, and compile other parts of the program only once. net P/invoke can call the corresponding DLL for different platforms.
L efficiency-as a system-level programming language on the. NET platform, C ++/CLI combines native and managed environments. Unlike C #, it can only perform managed programming. Therefore, C ++/CLI can be more efficient. The premise is that you are willing to write sensitive code with the highly efficient native C ++ (of course, who wouldn't want ?) In addition, because the standard C ++ is a static language, therefore, as a superset of Standard C ++, C ++/CLI can get more optimizations during compilation (static languages can always get more optimizations, because the compiler can know more information), it is more efficient. In contrast, C # compilation optimization is much weaker.
L hybrid programming environment-this is a unique programming environment for C ++/CLI. You can conduct efficient underlying development-corresponding to the Standard C ++ subset of C ++/CLI, or use managed programming to improve productivity where efficiency requirements are not so strict. Then they are smoothly linked together, and all of this is done in the programming language you are familiar with, you use the programming habits you are familiar with, the library you are familiar, familiar language features and styles... You do not need to learn a new language from the beginning, but do not need to face the issue of interaction between languages.
L habits-no one can underestimate the power of habits. For standard C ++ programmers. c ++/CLI is undoubtedly the preferred language for development on the net platform, because they have accumulated any programming skills and usage in Standard C ++, the library usage experience and code expression can all be "ported" to C ++/CLI. C ++/CLI maintains full compatibility with standard C ++ code, and provides the necessary semantics for programming in a hosted environment with minimal and consistent syntax extensions.
What do you need to change?
The simple answer is that there is almost no need to change. Yes, you can see the word "almost", but there is always some peace of mind: O) the fact is: porting the existing C ++ code to the C ++/CLI environment without any changes -- I used native C ++ to write a program, where STL was used, the lambda, MPL, and signal libraries in boost, and then I open the compilation option "/CLR" (or even "/CLR: Pure"). The result is that the program has completely passed the compilation. For programmers who use C ++/CLI for development, you need to be familiar with it. the programming paradigm on the net platform and the use of libraries. As for the various programming techniques and techniques of Standard C ++ programming that you were familiar with before, the use of various libraries -- just keep them!
So, to be exact, what you need is learning, not change.
C ++/cli-excellent mixed blood
The greatest success of C ++/CLI is the introduction of a hybrid programming environment, which is a very free environment where native and managed code can coexist and communicate with each other, so as to fully accept the Standard C ++ world, but also opens the door to another world...
The following are the key features of C ++/CLI extension --
Handle and gcnew -- Key to the managed World
Remember how managed classes are accessed in the managed C ++ extension world? The ugly _ GC keyword is everywhere-in fact, it is not just "ugly" (Why does Mc ++ disappear ?). In C ++/CLI, a new syntax element named handle is introduced, writing "^" -- you can think of it as the pointer in the managed World (but pointer arithmetic cannot be performed ).
Handle is used to hold the objects on managed heap. How can I create an object on managed heap? The original New is obviously unavailable, which will confuse its semantics. Therefore, C ++/CLI introduces a corresponding gcnew keyword. These two new syntax elements are the key to manipulating the managed World. Now, with handle and gcnew, you can communicate with any managed classes. In addition, since handle is the managed pointer, of course, for another important reason, in the managed world, there must also be a syntax element similar to that referenced by native -- that is, managed references "%" -- "^" corresponds to "*", and "%" corresponds to "&", in this way, at the syntax level, the syntaxes of pointers, references, and object creation on the stack are symmetric and consistent in the two worlds -- oh, and so on, as well as the unreferencing: the native pointer solution reference is "*". Out of the template's requirements for form uniformity, the handle solution reference is also "*". For example:
Somemanagedclass ^ handle = gcnew somemanagedclass (...);
Handle-> somemethod ();
Somemanagedclass % ref = * handle;
Now that gcnew is available, is there gcdelete? The answer is no-although they look symmetric. The reason is that for managed classes, there is no need to recycle the memory. But more importantly, the semantics of Delete is not only to reclaim memory. in a broad sense, delete is to recycle resources. In this sense, objects of the delete Managed class or native class are all meanings. Therefore, even if you need to delete your Managed class object to force it to release resources, you should also use Delete. At this time, the destructor of the Managed class will be called -- yes, managed classes also have destructor, which have the same semantics as dispose (). However, in C ++/CLI, you should not define dispose () functions for your managed classes, instead, you should always use the Destructor (the compiler will automatically generate the dispose () function based on the destructor), because the Destructor has one of the biggest advantages --
Deterministic destruction & raiI-a powerful tool for resource management
As every programmer familiar with the standard C ++ knows: raiI supported by the semantic guarantee of C ++ constructor and destructor ("resource acquisition is initialization" [2]) technology is a powerful tool for automatic and secure resource management. The resources here can include memory, file handle, mutex, and lock. By using raiI correctly, the code for managing resources can become incredibly elegant and simple. I believe that experienced C ++ programmers are familiar with the following statements:
Void F ()
Ofstream outf({out.txt ");
Out <"...";
} // Outf is analyzed here!
Here, programmers do not need to manually clear outf. When the function ends (outf is out of scope), outf will automatically analyze and release all its resources. Even if subsequent code throws an exception, the C ++ language can ensure that the Destructor will be called. In fact, after an exception is thrown, all locally constructed objects will be destructed during the stack unwind process. This provides a powerful and elegant way to manage resources in abnormal environments.
For C # or Java, the Code is not so elegant (especially Java) -- C # although the Using Keyword exists, the code is still bloated, java has to use an ugly and lengthy try-Finally block to ensure normal resource release in case of exceptions. When the situation becomes more complicated, C # and Java code will become increasingly bloated.
So, in C ++/CLI, is the original elegant method that relies on destructor to ensure correct resource release still exists? The answer is as powerful as what you expect and are familiar with, raiI can still be used, and it is still as powerful as standard C ++:
Ref struct d
D () {system: Console: writeline ("in D: D () \ n ");}
~ D () {system: Console: writeline ("in D ::~ D () \ n ");}
! D () {system: Console: writeline ("finalized! \ N ");}
Int main ()
D; // in D: D ()
} // D analyze the structure here! In D ::~ ()
The ref keyword indicates that the class is a Managed class. All ref classes are inherited from a public base class system: object. The difference between struct and class is still the same as that in Standard C ++. As you can see, for the ref class, you can define the Destructor as in Standard C ++, the Destructor will be called upon confirmation -- that is, when D is out of scope. Everything is consistent with your previous experience.
It is worth noting that for programmers who know Java or C #, The destructor of the ref class is dispose (). You do not have to and should not manually define a dispose () member function. So where is the finalize function? Since the ref class is created on the hosting stack, it will be recycled by GC sooner or later. Where is the finalize function to be called? C ++/CLI introduces a new syntax symbol "! D () ". This is D's finalize function. This"! D. The time when the function is called is uncertain. It depends on when the GC decides to recycle the space occupied by the class.
~ D () destructor have the same usage as standard C ++, releasing previously obtained resources. Right! D () uses the same rule as the finalize function. Because the call time is uncertain, do not rely on it to release key resources (such as file handles and locks ).
Introduce for ref class ~ D () and! D () greatly facilitates resource management and complies with the methods familiar to Standard C ++ programmers. Herb Sutter [3] regards this capability as one of the most powerful capabilities of C ++/CLI in the managed environment.
Pin_ptr -- fixed body
Never underestimate the capabilities of pin_ptr. It is a bridge between the native world and the managed World. Normally, GC is started at any time. Once GC is performed, the managed heap will be compressed and the object location will be moved, at this time, all handle pointing to the object will be updated. However, sometimes programmers want to pass the data (addresses) on the hosting stack to the Native Interface. For example, to reuse an efficient native algorithm or to do something else efficiently, in this case, normal native pointers are obviously not competent, because if native pointers are allowed to point to objects on the managed stack, these native pointers will point to the wrong location in the event of GC, cause serious consequences. The solution is to first "set" the object on the managed stack, and then pass the address to the native interface. This "fixed method" is pin_ptr -- It tells GC: do not move this object when compressing the heap!
Array <char> ^ arr = gcnew array <char> (3); // Managed class
Arr [0] = 'C ';
Arr [1] = '+ ';
Arr [2] = '+ ';
Pin_ptr <char> P = & arr [0]; // The entire arr is located on the stack.
Char * pbegin = P;
STD: Sort (pbegin, pbegin + 3); // reuse the native algorithm!
STD: cout <pbegin [0] <pbegin [1] <pbegin [2]; // output "++ C"
In the above Code, we reuse the sort algorithm in STL. In fact, with pin_ptr, We can reuse most native algorithms. This provides a way for us to build a compact and efficient program kernel.
It is worth noting that once the members of an object are on the stack, the object is on the stack. This is easy to understand, because moving an object is bound to move its members.
Another note is that pin_ptr can only point to certain types, such as basic types and value types. Because these types of memory la s are specific, accessing them through Native pointers will not cause unexpected consequences for native code. However, the memory layout of the ref class is dynamic, and the CLR can reorganize its layout for some optimizations (such as adjusting the data member layout to make better use of space ), this is no longer a static structure that the native world can understand. However, the main problem here is that the underlying Object Model of ref class is inconsistent with the object model of native world (for example, the structure of vtbl and the position of vptr ), therefore, it is a disaster to use the native pointer to accept the address of a ref class instance and call it. For this reason, the compiler strictly prohibits pin_ptr from pointing to the ref class instance.
Interior_ptr -- native pointer in the managed environment
Handle cannot perform pointer operations (because of its inherent semantic requirements, handle is faced with a managed environment that requires "security"), So handle has limited capabilities, it is not as powerful as the native pointer familiar to Standard C ++ programmers. In STL, iterator is a very powerful and efficient tool. Its underlying implementation usually uses native pointers. On the hosting stack, do we still have native pointers? Of course, the pointer in the shape of T * cannot be reused, because it cannot track the movement of objects hosted on the stack. Therefore, a new pointer form-interior_ptr is introduced in C ++/CLI. The semantics of interior_ptr and native pointers are almost identical, except that interior_ptr points to the managed heap. interior_ptr can be updated during GC. In addition, interior_ptr allows you to perform pointer operations and allow you to dereference them, everything and the native pointer are completely different. Interior_ptr provides a powerful and efficient tool for you to manipulate the data sequence (such as array) on the hosting stack. Therefore, the iterator mode can be copied to the hosting environment in the original version. For example:
Template <typename T>
Void sort2 (interior_ptr <t> begin, interior_ptr <t> end)
... // Sorting Algorithm
For (interior_ptr <t> PN = begin; PN! = End; ++ PN)
System: Console: writeline (* PN );
Int main ()
Array <char> ^ arr = gcnew array <char> (3 );
... // Assign values
Interior_ptr <char> begin = & arr [0]; // pointer to the header
Interior_ptr <char> end = begin + 3; // note that & arr [3] cannot be written, and the subscript is out of bounds.
Sort2 (begin, end); // sorting method similar to STL!
T *, pin_ptr, interior_ptr -- put them together
T *, pin_ptr, and interior_ptr are the three most important pointer forms in C ++/CLI. The relationships between them are as follows:
Powerful override Mechanism
In standard C ++, the virtual function rewriting mechanism is implicit. As long as the signatures of two functions are the same, and the functions of the base class with the same name are virtual functions, then, virtual function rewriting will occur no matter whether the function of the derived class is virtual or not. To some extent, this limits the user's ability to control its derived classes-the version of virtual functions is one of the problems. In C ++/CLI, you have the most powerful override mechanism, and you can more clearly express your intention, such as the following code:
Class B
Virtual void F ();
Virtual void g () abstract; // pure virtual function, which must be rewritten by the derived class; otherwise, the derived class is pure virtual class.
Virtual void H () sealed; // prevents the derived class from rewriting this function
Virtual void I ();
Class D: Public B
Virtual void F () New; // the new version of F, although the name is the same as B: F, B: F is not rewritten.
Virtual void H () override; // error! The sealed function cannot be rewritten.
Virtual void K () = B: I; // rewrite the name!
By correctly using these powerful override mechanisms, you can gain more powerful descriptive capabilities for class member functions to avoid unexpected implicit rewriting and version errors. However, it should be noted that "name-based" Rewriting is a powerful capability, but it should be used with caution. Improper use or misuse may lead to name disorder.
Value Type & sealing and unpacking
If you are from C #, I can almost hear your sigh. NET platform programming, you inevitably need to face the subtle differences between the value type and the reference type and the "crazy" implicit sealing-reference type (corresponding to ref class) the instance of is the first-class object, inherited from the common base class system: object, has a method table, object header, and so on. However, the value type (corresponding to value class) is extremely simple. It is similar to the pod [4] type in C ++. There is no method table or object class, and the value type should be allocated to the stack, when you use handle to hold a value-type instance, it will be implicitly blocked to the hosting stack (because handle must hold a first-class Object ), only when an instance of the value type is blocked to the stack can it have the first-Stream Object Features and can be referenced by object ^.
These are inherent features of. net, and all languages that use the. NET platform must comply with them. In this sense,. NET is indeed the top ruler of J. net.
Fortunately, the situation may not be as bad as you think, or perhaps better than in C #-because the handle syntax features in C ++/CLI are so obvious, so you can almost immediately find out where the binning will occur (even so, you still have to deal with some subtle situations). Let's look at an example:
Value Class V // The value keyword indicates that this is a value type, and the value type should be allocated on the stack.
{Int I ;};
V; // create a V instance on the stack
// Because V ^ must reference a "complete" object, that is, an object with a method table, metadata, and object header inherited from the system: Object public base class, therefore, V is implicitly sealed to the managed stack.
V ^ hv1 = V; // Note: Implicit packing!
V ^ HV2 = % v; // It is also a box seal! Using "%" to the value type will lead to a handle,
// So it will be sealed. This form is clear!
Hv1-> I = 10; // The value of Member I of V is not changed, but it is only the I in the object after the packing.
V = * hv1; // Unbox, and then copy it to the stack one by one. At this time, V. I is 10.
Here you may be aware of the problem-since handle is used to hold the value type will always cause it to be blocked to the hosting stack, in case I want to write a function and accept one (on the stack) what should I do if the value type instance is a real parameter and the value of its members is changed? If handle is used, what you direct to is not the original value but the object after the binning. It seems that you have changed its members, but actually changed the value of a "temporary" object! Therefore, handle should be removed from the second line here, where it is "%" (managed reference, corresponding to native reference -- "&") bind a hosting reference to a value type on the stack will not cause the binning operation. Let's look at an example:
Void adjust (V % ref_v)
Ref_v. I = 10; // change the ref_v member!
Int main ()
Adjust (V); // does not cause case sealing.
System: Console: writeline (v. I); // print 10
The principle is: to modify the value type instance on the stack, "%" is preferred, rather than "^ ". In this way, you will get the best efficiency and program correctness.
STL. net
STL is one of the most elegant and widely used libraries in Standard C ++. Standard C ++ programmers have accumulated a lot of experience in using STL. Of course, in the extended world of C ++/CLI, people also expect such libraries to be able to follow their long-known experiences and techniques. This is STL. net, STL for hosting the World! Stan Lippman [5] describes STL. Net primer's advantages in a concise manner on msdn [6].
Code Organization
Although C ++/CLI provides powerful capabilities, for people from the Standard C ++ community, they are more willing to isolate their standard C ++ code from the code that uses the C ++/CLI extension feature so that the former can be transplanted on different platforms, instead of binding to the CLI platform. After all, programming with C ++/CLI does not mean that all your code is related to the extension features of c ++/CLI. c ++/CLI is positioned as system-level programming, therefore, we can imagine that a large number of people will be very willing to use standard C ++ to write key efficiency code parts. For example, you can use standard C ++ to write efficient algorithms, these algorithms can be reused in other native environments. How can we isolate these standard C ++ code from the extension features of c ++/CLI? How to isolate it? The boundary between different compilation units is the best barrier-put your standard C ++ code in an independent header file and source file, put the code that uses the C ++/CLI extension in another header file and source file. In addition, try not to use CLI Syntax features in your native class, such as property, delegate, and index. Try not to let your native class inherit from ref class. In short, try to ensure that the code structure is clear and you will get the maximum degree of portability.
C ++/CLI is a pioneering initiative. It integrates the hosting environment and the native environment, and enables developers to have the powerful capabilities of "accessing the ground" at the same time. Obviously, Microsoft has spent a lot of effort on C ++/CLI. So that standard C ++ programmers can smoothly transition to C ++/CLI. The so-called smoothness is to ensure the original programming skills, habits, paradigm, and so on. It does. In the face of C ++/CLI, it is no longer a question about whether to learn or not, but how to make it exert greater energy.
[1] C ++/CLI standard download:
[2] See the C ++ programming language in Bjarne stroustrup.
[3] herb Sutter's blog: Author /.
[4] The plain old data type is simply a collection of data. There are no "C ++" features such as virtual functions, constructor, and destructor.
[5] Stan Lippman's blog:
[6] STL. Net Primer: Url =/library/en-US/dnvs05/html/stl-netprimer.asp


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.