Conversion Guide: Migrate the program from managed extended C to C / CLI
STANLEY B. Lippman Microsoft
Translation: Jiang Wei
August 2004
Suitable for: C / CLI second edition ISO-C
Abstract: C / CLI represents a dynamic model extension of an ISO-C language standard. This article lists the features of the V1 version language, and their correspondence in V2 versions (if present); and point out the language characteristics of the corresponding V1 feature. (68 Print Page)
Translator Note:
The original address is at http://msdn.microsoft.com/visualc/default.aspx?pull=/library/en-us/dnvs05/html/transguide.asp (English). This article has been published on the MSDN Chinese website, the URL is http://www.microsoft.com/china/msdn/library/langtool/vcpp/transguide.mspx. The MSDN version has a modification of the articles in the C / CLI language specification MSDN on the Microsoft website: http://msdn.microsoft.com/visualc/rs.xml
table of Contents
Introduction Language Keyword Tube Data Type Class or Member Declare Value Type and Its Behavior Language Change Overview
Appendix drives a revised language design
thank
Introduction
C / CLI represents a dynamic programming paradigm extension in the ISO-C standard language. There are many significant weaknesses in the original language design (V1), we feel that in the revised language design (V2) has been corrected. . This article lists the features of the V1 version language and their correspondence in V2 versions (if such a corresponding presence, it is pointed out as the language characteristics of the V1 feature that does not exist. For interested readers, the appendix is provided in the appendix. In addition, a source code level conversion tool (MSCFRONT) is being developed, and may provide people who want automated porting V1 code to new language design in the release of C / CLI. This article is divided into five chapters plus an appendix.
The first section discusses the main language keywords, especially the removal of the double downline and
Contextuality and
Segment keyword.
The second festival is on the change of the type of hosted data - especially hosting
Quote types and array types. It is also possible to find a detailed discussion of Deterministic Finalization. About changes in class members, such as attributes, index properties, and operators,
The focus of the third quarter.
Section II focused on the grammatical changes of hosting enumeration, internal and constraints. It also discusses many considerable semantic changes, such as implicit packing, hosted
CLI
Changes of enumeration, and
The support of the value class default constructor is removed.
The fifth section has a bit like hodgepodi - a famous wolf
Miscellaneous. You can find behavior and parameters for type conversion symbols, string constants in it.
Discussion of arrays.
Language keyword
A general conversion of the original version to the revision is to remove the double underscore in all keywords. For example, an attribute is now declared as Property instead of __property. Two main reasons for using double-downline prefix in the original design is:
This is a method of providing a language extension that meets ISO-C standards. One of the main purposes of the original language design is the incompatibility of untrunnation and standard languages, such as new keywords and tags. This reason greatly promotes the choice of pointer syntax for the statement of the recorded type of custodial reference. Double underscore use, in addition to compatibility, it is also a reasonable guarantee that does not affect the user's basic code. This is the second main purpose of the original design.
In this case, why do we remove dual underline (and introduce some new tags)? No, this does not mean that we will no longer consider and maintain consistent! We continue to be consistent with standards. Despite this, we realized that the support of the CLI dynamic object model exhibited a new powerful programming model. Our experience in the original design and the design and development of C make us convinced that the support of this new model requires its own advanced keywords and tags. We want to provide a new model of first-class expression, integrating it and support standard languages. We hope that you will feel the revised language design provides a first-class programming experience for these two distinctive object models.
Similarly, we care about minimizing the impact of these new keywords to existing code. This is solved with contextual and segment keywords. Before we look at the revision of the actual language grammar, let's try to figure out the flavor of these two special keywords.
A contextual keyword has a special meaning in a particular locale. For example, in a usual program, Sealed is identified as a normal identifier. However, in a statement of a managed category type, it is identified as a keyword in the category context. This minimizes the potential impact of introducing a new keyword in the language, and we feel that this is very important for users with our old code base. At the same time, it allows new features to get a first-class new language characteristic experience - we are missing these factors in the original design. We will see Sealed usage in Sea in Sea.
A segment key is a special case for contextual keywords. Field is a contextual modifier and an existing keyword pair, separated by space. This pair is identified as a syntax unit, such as a Value Class (see 2.1), not two separate keywords. Based on reality, this means a macro of redefining the value of value, as shown below:
#ifndef __cplusplus_cli
#define value
Will you remove Value in a class declaration. If you do so, you have to redefine the syntax pair as described below:
#ifndef __cplusplus_cli
#define value class class
This is necessary to take into account the factors of reality. Otherwise, existing #define may convert the contextual keyword section of the segment keyword. (Translator Note: For example, in January 2003, the #define interface structure in the platform SDK header file, see http://blog.joycode.com/jiangsheng/archive/2004/12/17/41283.aspx).
2. Managed data type
Declaring managed data types and creation, and using these types of objects have been greatly modified to increase compatibility to ISO-C type systems. These changes are detailed in the subsequent section. The discussion of the entrustment delayed from Section 3.3 to express them in an event member of the class - this is the topic of Section 3. (About more detailed tracking quotation grammar introduces the discussion of the main transformation in insider and design, see the appendix promotion revision language design.)
2.1 Declare a managed class type
In the original language definition, a reference class is starting with the __gc key. In the revised language, __ gc keyword is replaced by one of two segment key Ref classes or REF STRUCT. Struct or Class selection only indicates the default disclosure of the portion that does not explicitly accessed at the beginning of the type body (for struct) or private (for Class) default access level.
Similarly, in the original language design, a reference class is starting with the __value keyword. In the revised language, __ value keyword is replaced by one of two segment key Value Class or Value Struct.
In the original language design, an interface type is indicated by keyword __interface. In a revised language, it is replaced by Interface Class. For example, the following statement set
// original grammar
Public __gc class block {...}; // reference class
PUBLIC __VALUE CLASS Vector {...}; // value class
Public __interface iMyfile {...}; // Interface class
The equivalent declaration under the revised language design is as follows:
// Rev. syntax
Public Ref class block {...};
Public value class vector {...};
Public interface class iMyfile {...};
The idea that REF (for reference types) instead of GC (for garbage collection) is to better imply the essence of this type.
2.1.1 Specify a class as an abstract type
In the original language definition, the keyword __abstract can be placed before the type keyword (before __gc) to indicate that the class has not been completed, and such objects cannot be created in the program:
PUBLIC __GC __ABSTRACT CLASS Shape {};
PUBLIC __GC __ABSTRACT CLASS Shape2D: Public Shape {};
In the revised language design, the Abstract contextual keyword is defined after the class name, the class, the base class is derived before or the semicolon.
Public Ref class shape abstract {};
Public Ref class shape2d abstract: public shape {};
Of course, the semantics have not changed.
2.1.2 Specify a class as a closed type
In the original language definition, the keyword __sealed is placed before the class keyword (before __gc) to indicate that the class cannot be inherited:
PUBLIC __GC __SEALED CLASS STRING {};
In the V2 language design, the Sealed contextual keyword is limited to the class name, the class, the base class derived list or semicolon (you can close it while declaring a inherited class. For example, String class implicitly Self Object). The advantage of enclosing a class is to allow static (that is, when compiling, the object calls for this seal reference type object are parsed. This is because the sealing indicator guarantees the String tracking handle that cannot point to a derived class object that may overload the triggered false method.
Public Ref class string sealed {};
You can also declare a class that is also declared as a closed class. This is a special case called a static class. This is described below in the CLI documentation
At the same time, you can only have a static member for abstract and closed types, and the same manner as the namespace is called in some languages.
For example, this is a statement of abstract closed class using V1 syntax
Public __gc __sealed __ABSTract CLASS STATE
{
PUBLIC:
STATIC state ();
Static Bool INPARAMLIST ();
Private:
Static bool ms_inparam;
}
This is this statement in the revised language design:
Public Ref Class State Abstract Sealed
{
PUBLIC:
STATIC state ();
Static Bool INPARAMLIST ();
Private:
Static bool ms_inparam;
}
2.1.3 CLI inheritance: Specify the base class
In the CLI object model, only single inheritance of public mode is supported. However, in the original language definition, ISO-C explains the base class is still retained. The base class without access to the keyword will be a private derived type by default. This means that every CLI inheritance declares have to replace the default explanation with a public keyword. Many users think that the compiler seems to be too rigorous. // v1: Error: The default is privately derived
__gc class my: file {};
In the revised language definition, the CLI inheritance defines the lack of access to the keyword, default is derived in public way. In this way, the public access key is no longer necessary, but optional. Although this change does not need to make any modifications to V1, I will still list this change for integrity.
// v2: Correct: By default is the publicity derived
Ref class my: file {};
2.2 References of a CLI reference object
In the original language definition, a reference class type object is based on the ISO-C Pointer syntax, and an optional __gc keyword is used on the left side of the asterisk. For example, this is a statement of multiple reference type types under V1 syntax:
PUBLIC __GC CLASS FORM1: PUBLIC System :: Windows :: Forms :: form {
Private:
System :: ComponentModel :: Container__gc * Components;
Button __gc * button1;
DataGrid __gc * mydatagrid;
Dataset __gc * mydataset;
Void PrintValues (array * myarr)
{
System :: Collections :: ienumerator * myenumerator =
Myarr-> getenumerator ();
Array * localarray = myarr-> copy ();
// ...
}
}
In the revised language design, the reference class type object is declared with a new declarative symbol (^), formally expressed as a tracking handle, and more unfair expression is a hat. (Tracking this adjective emphasizes that the reference type object is located in the CLI heap, so it can be transparently moving in the stack of garbage collection compression processes. A track handle is transparently updated during runtime. Two similar concepts: (a) Track reference (%) and (b) internal pointers (interior_ptr <>), discussed in Section 4.4.3.
Declaration syntax no longer reuses ISO-C pointer syntax has two main reasons:
The use of the pointer syntax does not allow the operator to be reused to reference objects; there is to call the internal name of the operator, such as RV1-> op_addition (RV2) instead of more intuitive RV2 RV2. Many pointer operations such as type forced conversion and pointer arithmetic are invalid for objects located on the garbage collection stack. We feel that the concept of a track handle is preferably in line with the nature of a CLI reference type.
Use the __gc modifier for a tracking handle and is unnecessary and not supported. The usage of the object itself has not changed, which is still accessing the member (->) accessed by the pointer member. For example, this is the result of the above V1 text translation to the revised language syntax:
Public Ref Class Form1: Public System :: Windows :: Forms :: form {
Private:
System :: ComponentModel :: Container ^ Components;
Button ^ Button1;
DataGrid ^ MyDataGrid;
Dataset ^ myDataSet;
Void PrintValues (array ^ myarr)
{
System :: Collections :: ienumerator ^ myenumerator =
Myarr-> getenumerator (); array ^ localarray = myarr-> copy ();
// ...
}
}
(Translator Note: ^ Reference The entire object in the hosted stack cannot be used to point to the inside of the type.)
2.2.1 Dynamic allocation objects on the CLI pile
In the original language design, the existing NEW expressions allocated on conventional stacks and hosted stacks are largely transparent. In almost all cases, the compiler can correctly determine the conventional stack or hosted stacks from the context. E.g:
Button * Button1 = new button; // Good: House
INT * pi1 = new int; // Good: Traditional Pile
INT32 * PI2 = New Int32; // Good: Household
The result of the contextual heap allocation is not expected, and the __gc or __nogc keyword guide compiler can be used. In a revised language, use the newly introduced GCNEW keyword to significantlyify the different nature of the two New Expressions. For example, the above declarations look like this in the revised language:
Button ^ Button1 = gcnew button; // Good: Household
INT * pi1 = new int; // Good: Traditional Pile
Interior_ptr
(Discussing more detail of Interior_PTR in Section 4. Usually, it represents an address of an object, this object does not have to be on the hosted stack. If the point to which the object is indeed located, it is transparent when the object is repositioned Update)
This is the initialization of the Form1 member V1 version declared in front section:
void initializecomponent ()
{
Components = New System :: ComponentModel :: Container ();
Button1 = new system :: windows :: forms :: button ();
MyDataGrid = new datagrid ();
Button1-> Click =
New system :: EventHandler (this, & form1 :: button1_click);
// ...
}
This is the same initialization process rewritten with the revised syntax, and the reference type is the result of a GCNEW expression that does not require a "hat".
void initializecomponent ()
{
Components = GCNEW System :: ComponentModel :: Container
Button1 = gcnew system :: windows :: forms :: button;
MyDataGrid = GCNew DataGrid;
Button1-> Click =
GCNEW System :: EventHandler (this, & form1 :: button1_click);
// ...
}
2.2.2 an empty object tracking reference
In a new language design, 0 no longer represents an empty address, but is processed as an integer, like 1, 10, 100, so that we need to introduce a special mark to represent an null value tracking reference. For example, in the original language design, we will initiallyify a reference type as an empty object reference:
/ / Correct: We set OBJ not to reference any object
Object * Obj = 0;
// Error: No implicit box
Object * Obj2 = 1; In the revised language, any initialization or assignment of any slave value to an Object causes an implicit packing of a value type (Implicit Boxing). In the revised language, OBJ and OBJ2 are initialized to pack-over INT32 objects, each having values 0 and 1, respectively. E.g
// lead to implicit boxes of 0 and 1
Object ^ obj = 0;
Object ^ obj2 = 1;
Therefore, in order to allow explicit initialization, the value is assigned a tracking handle, and we introduce a new keyword, NULLPTR. The correct revision of the V1 example appears as follows:
/ Ok: We set up OBJ does not quote any object
Object ^ obj = nullptr;
/ Ok: We initialize Obj as an int32 ^
Object ^ obj2 = 1;
This makes the transplant designed from the existing V1 code to the revised language. For example, consider the following declaration:
__Value struct holdingr {// original V1 syntax
Holder (Continuation * C, SEXPR * V)
{
CONT = C;
Value = v;
Args = 0;
ENV = 0;
}
Private:
CONTINUATION * CONT;
Sexpr * Value;
Environment * ENV;
Sexpr * args __gc [];
}
Here, Args and ENV are CLI reference types. In the process function, the statement that initializes it is 0 must be modified to Nullptr during the transfer to a new grammatical process.
// Revision V2 syntax
Value Struct Holder
{
Holder (Continuation ^ C, SEXPR ^ V)
{
CONT = C;
Value = v;
Args = NULLPTR;
ENV = NULLPTR;
}
Private:
CONTINUATION ^ Cont;
SEXPR ^ Value;
Environment ^ ENV;
Array
}
Similarly, these members and 0 comparisons must also be changed to NullPtr comparison. This is the original syntax:
// Original V1 syntax
Sexpr * loop (Sexpr * Input)
{
Value = 0;
Holder Holder = Interpret (this, Input, ENV);
While (Holder.Cont! = 0)
{
IF (Holder.env! = 0)
{
Holder = interpret (Holder.cont, Holder.Value, Holder.env);
}
Else IF (Holder.Args! = 0)
{
Holder =
Holder.Value-> Closure () ->
Apply (Holder.cont, Holder.Args);
}
}
Return Value;
}
And here is a revised edition. Converting each 0 to Nullptr (the translation tool is helpful for this conversion, automatically handles many - if not all - instances, including using null macros.
// Revision V2 syntax
SEXPR ^ loop (sexpr ^ input)
{
Value = NULLPTR;
Holder Holder = Interpret (this, Input, ENV);
While (Holder.Cont! = Nullptr)
{
IF (Holder.Env! = nullptr) {
Holder = interpret (Holder.cont, Holder.Value, Holder.env);
}
Else if (Holder.Args! = nullptr)
{
Holder =
Holder.Value-> Closure () ->
Apply (Holder.cont, Holder.Args);
}
}
Return Value;
}
Nullptr can be converted into any tracking handle type or pointer, but it cannot be upgraded to a integer type. For example, in the initialization set, NullPtr is only valid in both initial values at the beginning.
/ / Correct: We set OBJ and PSTR do not quote any object
Object ^ obj = nullptr;
Char * pstr = nullptr; / / 0 can also be used here
// Error: There is no conversion from Nullptr to 0 ...
INT IVAL = NULLPTR;
Similarly, a given method set is as follows:
Void f (Object ^); // (1)
Void f (char *); // (2)
Void f (int); // (3)
The call to use Nullptr is as follows
// Error: Ambiguity: Match (1) and (2)
f (Nullptr);
It is ambiguous because NullPtr matches a tracking handle and matches a pointer, and there is no priority selection in both (this requires an explicit type forced conversion to eliminate ambiguity).
A call using 0 is just matching examples (3):
/ / Correct: Match (3)
f (0);
Since 0 is integer. When there is no F (int), it matches F (char *) through a standard conversion. When there is no precise match, the standard conversion is given to the priority of implicit packments for value types. This is why there is no ambiguity here.
2.3 CLI array declaration
The statement of the CLI array in the original language design is a little unertally expanded by the standard array statement. A __GC keyword is placed between the array object name and the possible comma-filled dimensions, as shown in the following example:
// v1 syntax
Void PrintValues (Object * myarr __gc []);
Void PrintValues (int myarr __gc [,]);
This is simplified in the revised language design. We use a vector declaration that mimic STL similar to template. The first parameter specifies the element type. The second parameter specifies the array dimension (the default is 1, so only the multi-dimensional array requires the second parameter). Array object itself is a tracking handle, so a hat must be given. If the element type is also a reference type, they must also be marked. For example, the above example, when it is expressed in a revised language, it looks like this:
// v2 syntax
Void PrintValues (Array
Void PrintValues (Array
Since the reference type is a tracking handle instead of an object, a CLI array type is might for use for the return value type (the traditional array cannot be used for function return value). In the original language, its grammar is a bit untrustworthy. E.g:
// v1 syntax
INT32 f () [];
Int getArray () __GC [];
In V2, this statement is simple to read and analyze. E.g:
// v2 syntax
Array
Array
The simple initialization of traditional hosted arrays is supported in two versions of language. For example // v1 syntax
INT getArray () __gc []
{
INT A1 __GC [] = {1, 2, 3, 4, 5};
Object * myobjarray __gc [] = {
__box (26), __box (27), __box (28), __BOX (29), __box (30)
}
// ...
}
It is greatly simplified in V2 (note that because the packing in the revised language design is implicit, __ box operators are removed - see section 4 on its discussion.
// v2 syntax
Array
{
Array
Array
// ...
}
Since array is a CLI reference type, each array object declaration is a tracking handle. Therefore, it must be assigned on the CLI pile (concise initialization hides the details of the assignment on the hosted "). This is an explicit initialization form of an array object in the original language design:
// v1 syntax
Object * myarray [] = new object * [2];
String * mymat [,] = new string * [4, 4];
Recalling, in the new language design, the New expression is replaced by GCNew. The dimension of the array is passed to the GCNEW expression as the parameter:
// v2 syntax
Array
Array
In the revised language, the GCNEW can be followed by a explicit initialization list, which is not supported in the V1 language, for example:
// v2 syntax
// Explicit Initialization List Follow GCNew
// is not supported in v1
Array
Gcnew array
2.4 Changes in semantics semantics
In the original language definition, the destructor of the class allows existence in the reference class, but it is not allowed to exist in the value class. This has not changed in the V2 language design, but the semantics of the destructuring function have a significant change. How to change (and this will affect the translation of the existing V1 code) is the topic of this section. This may be the most complicated section in this article, so we will talk slowly. This may also be the most important programming level between two language versions, so it is worthwhile to step by step.
2.4.1 Uncertain endization
Before the memory associated by the object is recovered by the garbage collection machine, if the object has a related femalize () method exists, then it will be called. You can imagine it as a super destructor because it is independent of the object programming life. We call this ending. When or even if it is called a femalize () method is uncertain. This is what we express when garbage collection represents the endization of Non-Deterministic Finalization.
Uncertain ending and dynamic memory management cooperation is very good. The garbage collection machine is involved when the memory is lacking to some extent, and work well. In the memory collection environment, it is unnecessary to release the memory with the patterned function. When you first start writing this program, you don't worry about the potential memory leakage, but it will adapt to this mechanism.
However, uncertain ending mechanisms are maintained in a key resource, such as a database connection or a type of lock is not working. In this case we need to release resources as soon as possible. In the conventional code environment, this is solved with a constructor. Whether it is a native code block that performs a declaration object or a retention of an abnormality, the life period of the object is terminated, and the destructive function is intervened and the resource is automatically released. This mechanism is operated very well, and there is a big mistake in the original language design. The solution provided by the CLI is a class that implements the Dispose () method of the IDisposable interface. The problem here is that the dispose () method requires a user to explicitly call. This is a mistake of the wrong and is therefore a reverse. The C # language provides a moderate automation, using a special USING statement. Our original language design - I have already mentioned that there is no special support at all.
2.4.2 Action function in V1 goes to Finalize ()
In the original language, a reference class is implemented in the following two steps:
The destructure function written by the user is renamed Finalize (). If the class has a base class (remember, only single inheritance in the CLI object model), the compiler is inserted into the final call of the base class after the user's code. For example, given a normal level in the following V1 language specification__gc class a {
PUBLIC:
~ A () {console :: WriteLine (s "in ~ a");}
}
__GC Class B: Public a {
PUBLIC:
~ B () {Console :: WriteLine (s "in ~ b");}
}
The two destructor are renamed Finalize (). B Finalize () adds a finalize () call after WriteLine (). These are the code that the garbage collection machine is called by default during the ending process. Its internal conversion results may like this to: // v1 internal conversion of the destructor
__gc class a {
PUBLIC:
Void finalize () {console :: WriteLine (s "in ~ a");}
}
__GC Class B: Public a {
PUBLIC:
Void finalize () {
Console :: WriteLine (s "in ~ b");
A :: finalize ();
}
}
In the second step, the compiler summarizes a false argument function. This destructor is the program of our V1 user directly call or is called via the delete expression. It will never be called by garbage collection. What is this summary? Is two statements. One is to call GC :: SuppressFinalize () to ensure subsequent calls for the femalize () method for this object. Another is actually Finalize () call. Recall, this expresses the destructor of this class provided by the user. It seems like this: __ gc class a {
PUBLIC:
Virtual ~ a ()
{
System :: GC :: SuppressFinalize (this);
A :: finalize ();
}
}
__GC Class B: Public a {
PUBLIC:
Virtual ~ b ()
{
SYSTEM :: GC: SuppressFinalize (THIS);
B :: finalize ();
}
}
This implementation allows the user to immediately explicitly trigger the Finalize () method of the class rather than calls the garbage collection machine, which does not really depend on the solution using the Dispose () method. This is changed in the revised language design.
2.4.3 V2 Action Function Go to Dispose ()
In the revised language design, the destructor is renamed to Dispose (), and the class is automatically extended to implement the IDisposable interface. In other words, our pair of classes are converted as follows:
Internal conversion of the destructor in // v2
__GC Class A: IDisposable {
PUBLIC:
Void Dispose () {
System :: GC :: SuppressFinalize (this);
Console :: WriteLine ("in ~ a");
}
}
__GC Class B: Public a {
PUBLIC:
Void Dispose () {
System :: GC :: SuppressFinalize (this);
Console :: WriteLine ("in ~ b");
A :: dispose ();
}
}
When the monograph function is called, whether by explicitly calling in V2 or by applying delete, the underlying DISPOSE () method is automatically called. If this is a derived class, a call to the base class DISPOSE () method is inserted to the end of the summary method.
But this did not give us a method of determining the endization. In order to solve this problem, we need additional support for local reference objects (there is no similar support in the original language design, so there is no translation issue).
2.4.4 Declare an reference object
The revision language supports objects on a local stack or a declaration of a class member that can be directly accessed (note that this is not available in the Beta1 release of Microsoft Visual Studio 2005). When the desppee () method described in Section 2.4.3 is combined, the result is the automatic call of the reference type end statement. This tyrannosaurus finally tamed, at least for C / CLI users at least for C / CLI users. Let us see and understand what this means.
First, we define our reference classes so that the object creation code gets a resource in the class constructor. Second, in the destructor of the class. We release the resources obtained when the object is created
Public Ref class r {
PUBLIC:
R () {/ * Get external resources * /}
~ R () {/ * Release the external resource * /}
// ... 杂 八 ...
}
The object is declared as part, and the use of the type name that does not have a "hat". All objects of objects, such as calling a member function, is through member selection points (.) Instead of arrow (->). At the end of the block, the associated destructor converted to Dispose () is automatically called.
Void f ()
{
R r;
R.MethodCall ();
// ...
// r is automatically destructure -
// That is, R.Dispose () is called ...
}
Compared to the USING statement in the C #, this is just a meaning on the syntax instead of the CLI's fundamental agreement - all reference types of objects must be assigned on the CLI pile - challenge. Basic syntax has not changed. The user may have been equivalentously written (this is very like internal conversion executed by the compiler):
/ / Equivalent implementation ...
/ / In addition to it should be outside a try / finally statement
Void f ()
{
R ^ r = gcnew r;
R-> MethodCall ();
// ...
Delete R;
}
In a revised language design, the destructive function is again and constructed in a functional mechanism for obtaining / releasing resources associated with a local object lifetime. This is a significant and very shocking achievement, and language designers should be praised.
2.4.5 Declare an explicit finalize () - (! R)
In the revised language design, the constructor is integrated into a Dispose () method as we have seen. This means that the garbage collection machine, the garbage collection machine is not similar to the object to find the associated Finalize () method as the object is not explicitted when the descriptive function is explicitly called. In order to support the destructor and ending, the revised language introduces a special syntax to provide a finalization. For example: public ref class r {
PUBLIC:
! R () {Console :: WriteLine ("I am the r :: firmizer ()!");}
}
! Prefix intends to imply the symbols (~) used in the same introduction of the destructuring function, that is, the names of the two life periods are in front of the class name plus a symbol prefix. If there is a summary Finalize () method in the derived class, the call of the Finalize () method of a base class will be added at the end. If the destructor is explicitly called, the endization will be suppressed. This conversion can look like this:
Internal conversion in // v2
Public Ref class r {
PUBLIC:
void finalize ()
{Console :: WriteLine ("I am the r :: firmizer ()!");
}
2.4.6 What does this mean in the conversion of V1 to V2?
This means that as long as an reference class contains a special destructor, a V1 program is secretly modified under the V2 compiler. The translation algorithm needed looks as follows:
If the parses function exists, rewritten it is a class ending method. If the Dispose () method exists, rewritten into the classified function. If the constructor is present but the dispose () method does not exist, the parses function is reserved and the subkey is executed (1)
This conversion may miss this conversion from the process of transplant your code to V2 from V1. If the reference program depends somewhat depends on the execution of the relevant ending method, the behavior of the application will be secretly modified.
3. Members in class or interface
The declaration of attributes and operators has been rewritten in a large range in the revised language design, hiding the underlying event details exposed in the original design. In addition, the event statement has also been modified.
One of the revisions that V1 in V2, the static constructor can now be defined outside of the class (they must be defined in line in V1), and introduce a sign of a delegate constructor.
3.1 Property declaration
In the original language design, each set or get attribute access method is specified as a stand-alone member function. The declaration of each method has the __property keyword as a prefix. The method name begins with SET_ or GET_, the actual name of the rear connection attribute (the one seeing the user). In this way, a attribute access method of a vector-grooming X coordinate will be named GET_X, and the user will access it in Name X. This name conversion and a separate method specify that it actually reflects the underlying implementation of the operating time of the attribute. For example, this is our vector, with some coordinate properties:
PUBLIC __GC __SEALED CLASS Vector
{
PUBLIC:
// ...
__property double get_x () {return _x;}
__property double get_y () {return_y;}
__property double get_z () {return_z;}
__property void set_x (double newx) {_X = newx;}
__property void set_y (double newy) {_y = newy;
__property void set_z (double newz) {_z = newz;}
}
This is found to be confused, because the attribute-related function is deployed and requires the user to unify SET and GET from the syntax. And it is too lengthy on the grammatical, and it feels not very elegant. In the revised language design, this statement is more similar to the type of the C # - Property keyword and the original name of the attribute. Set access and GET access methods are placed in a section of the attribute name. Note that the symbol of the method of access is pointed out as C #. For example, this is the above code under a new language design: Public Ref class vector sealed
{
PUBLIC:
Property Double X
{
Double get ()
{
Return_x;
}
Void Set (Double Newx)
{
_x = newx;
}
} // NOTE: No Semi-Colon ...
}
If the access method of the two properties is characterized by different access levels - such as a public GET and a private or protected SET, you can specify an explicit access flag. By default, the attribute's access level is around its access level. For example, in the above VECTOR definition, GET and SET methods are public. In order to make the set method become protected or private, you must modify the definition as follows
Public Ref class Vector Sealed
{
PUBLIC:
Property Double X
{
Double get ()
{
Return_x;
}
Private:
Void Set (Double Newx)
{
_x = newx;
}
} // Note: Private's scope to end ...
// Note: DOT is a multi-way public method ...
Double Dot (const vector ^ wv);
// ETC.
}
Accessing keywords in the property extends to the end of the attribute or the declaration of another access key. It does not extend to the definition of attributes. For example, in the above declaration, Vector :: DOT () is a public member function.
Write the set / get attribute for the three vector coordinates, because the nature is dead: (a) declare a private state member with an appropriate type, (b) return when the user wants to get its value, and (c) Set it to any value you want to assign. In a revised language design, a simple attribute syntax can be used to automate this way of use:
Public Ref class Vector Sealed
{
PUBLIC:
/ / Simple attribute syntax
Property Double X;
Property Double Y;
Property Double Z;
}
The interesting side effects of the concise attribute syntax is that when the compiler automatically generates the background state member, this member is internally internally internally inseparable from the SET / GET access. This is the so-called strict limit of data hidden!
3.2 Index Attribute Statement
The two main disadvantages supported by the original language on index properties cannot provide a class-level subscript, that is, all index properties must have a name. For example, there is no way to provide a managed subscript operator that can be applied directly to an object of a vector or Matrix class. Second, a secondary disadvantage is that it is difficult to distinguish the attribute and index properties - the number of parameters is the only judgment method. Finally, the index attributes have the same problem with non-index properties - the access function is not identified as a basic unit, but is divided into a separate method. for example:
PUBLIC __GC CLASS.
Public __gc class matrix
{
Float Mat [,];
PUBLIC:
__property void set_item (int R, int C, float value);
__property int GET_ITEM (int R, INT C); __property void set_row (int r, vector * value);
__property int GET_ROW (INT R);
}
As you can see, you can only specify a two-dimensional or single-dimensional index with additional parameters. In the revision syntax, the index is characterized by the number of square brackets ([,]) and the number of the number and type of the flag of each index:
Public Ref class vector;
Public Ref Class Matrix
{
Private:
Array
PUBLIC:
Property Int item [int, int]
{
INT GET (INT R, INT C);
Void Set (int R, int C, Float Value);
}
Property int Row [INT]
{
INT GET (INT R);
Void Set (int R, Vector ^ Value);
}
}
In the revised syntax, in order to specify a class level of the object that can be applied to the class, the default keyword is used to replace an explicit name. E.g:
Public ref class matrix {private: array
In the revision syntax, when the default index property is specified, the following two names are retained: GET_ITEM and SET_ITEM. This is because they are the underlying name generated by the default index property.
Note that simple index syntax and simple attribute syntax are very different.
3.3 Entrusted and events
Declare that only changes in a delegate and normal events are to remove double underscores, as described below. After removing, this change is considered to be completely unconventional. In other words, no one supports keeping the double underscore, everyone now seems to agree that the double underline makes the language difficult.
// Original language (V1)
__dlegate void ClickeVentHandler (int, double);
__delegate void dblclickeventhandler (string *);
__GC class eventsource {
__Event ClickeventHandler * Onclick;
__event dblclickeventhandler * Ondblclick;
// ...
}
// Revised language (V2)
Delegate Void ClickeventHandler (int, double);
Delegate void dblclickeventhandler (String ^);
Ref class eventsource
{
Event Clickeventhandler ^ onclick;
Event dblclickeventhandler ^ onDblclick;
// ...
}
Events (and delegates) are reference types, which is more obvious in V2 because there is a hat (^). In addition to ordinary forms, events support an explicit declarative syntax, and users specify an event associated with ADD (), ras (), and remove () methods in explicitly. (Only the add () and remove () methods are must be; the RAISE () method is optional). In the V1 design, if the user selects these methods, even if she must decide the name of the incident that is still there, she also implements an explicit event statement. The separate method identifies the format of add_eventname, raise_eventname, and remove_eventname, as described in the example of V1 language specification, as described below:
// Original V1 language
// Explicitly achieve add, remove and raise ...
Public __delegate void f (int);
PUBLIC __GC STRUCT E {
f * _e;
PUBLIC:
E () {_e = 0;}
__event void add_e1 (f * d) {_e = D;
Static void Go () {
E * PE = New E;
PE-> E1 = New f (PE, & E :: Handler);
PE-> E1 (17);
PE-> E1 - = New f (PE, & E :: Handler);
PE-> E1 (17);
}
Private:
__event void raise_e1 (int i) {
IF (_e)
_E (i);
}
protected:
__event void remove_e1 (f * d) {
_E - = D;
}
}
The problem of this design is mainly sensory, not functional. Although language design supports these methods, the above example does not look at it. Because the existence of V1 attributes and index properties, the method in class declaration looks thousands of holes. A slightly frustrating is the lack of an actual E1 event statement. (The underlying implementation details are once again exposed in the syntax characteristics of the user level, obviously adding the complexity of the grammar.) This is just labor and reactive. V2 design greatly simplifies this statement, as described below. Two or three methods are specified in a pair of curly brackets after the event declaration and its related commission types:
// Revised V2 Language Design
Delegate Void F (int);
Public Ref struct e {
Private:
f ^ _e; // Yes, the commission is also a reference type
PUBLIC:
E ()
{// Note 0 is changed to nullptr!
_E = NULLPTR;
}
// V2 syntax collection of the display event declaration
Event f ^ e1
{
PUBLIC:
Void Add (f ^ D)
{
_E = D;
}
protected:
Void Remove (f ^ D)
{
_E - = D;
}
Private:
Void Raise (INT i)
{
IF (_e)
_E (i);
}
}
Static void Go ()
{
E ^ pe = gcnew e;
PE-> E1 = GCNEW F (PE, & E :: Handler);
PE-> E1 (17);
PE-> E1 - = GCNEW F (PE, & E :: Handler);
PE-> E1 (17);
}
}
Although in terms of language design, people tend to ignore it because of the simple boring of grammar, but if there is a very subtle influence on the language's user experience, it is actually very meaningful. A confused inefficient syntax increases the risk of development processes to a large extent, just like a dirty or unclear windshield to increase the risk of driving. In a revised language design, we tried to transparently transparently like a highly polished newly mounted windshield. 3.4 Sealed a virtual function
__sealed keyword is used in V1 to modify a reference type, which is forbidden to continue derived - like we see in Section 2.1.2 - or modify a virtual function, prohibiting continued overload from this. for example:
Class base {public: Virtual void f ();
Class Derived: public base {
PUBLIC:
__sealed void f ();
}
In this example, derived :: f () overloads the Base :: F () instance based on the full match of the function type. __sealed keyword indicates that a subsequent class inherited from the DeriveD cannot implement a Derived :: F () overload.
In a new language design, Sealed is after the name of the name, which allows any location before the actual function prototype. In addition, SeaRed uses also requires simultaneous use of an explicit Virtual keyword. In other words, the correct translation of Derived above is as follows:
Class Derived: Public Base
{
PUBLIC:
Virtual void f () sealed;
}
An error will occur if the virtual keyword is missing. In V2, context keyword Abstract can be used in = 0 to indicate a pure virtual function. This is not supported in V1. for example:
Class base {public: Virtual void f () = 0;
Can you write so?
Class base {public: Virtual void f () Abstract;
3.5 Operation Overload
It may be the most amazing aspect of the original language design is its support for operator - or more appropriately, it is a significant lack of support. For example, in a declaration of a reference type, it is not built-in Operator syntax, but the underlying internal name of the explicit writing operator - OP_ADDITION in this example. However, more trouble is, the operation of the operator must be explicitly triggered with this name, which hinders two main benefits of operator overload: (a) intuitive grammar, and (b) mixed existing types and The ability of new types. for example:
PUBLIC __GC __SEALED CLASS vector {
PUBLIC:
VECTOR (Double X, Double Y, Double Z);
Static Bool Op_equality (const vector *, const vector *);
Static vector * op_division (const vector *, double);
Static Vector * OP_Addition (const vector *, const vector *);
Static Vector * OP_SUBTRAction (const vector *, const vector *);
}
int main ()
{
Vector * PA = New Vector (0.231, 2.4745, 0.023);
Vector * Pb = New Vector (1.475, 4.8916, -1.23);
Vector * pc1 = Vector :: op_addition (pa, pb);
Vector * PC2 = Vector :: op_subtraction (PA, PC1); Vector * PC3 = Vector :: op_diVision (PC1, PC2-> x ());
IF (Vector :: op_equality (pc1, p2)))
// ...
}
In the revision of the language, the ordinary expectations of traditional C programmers are restored, declare and use built-in operators. Here is a VECTOR class that translated into V2 grammar:
Public ref class vector sealed {
PUBLIC:
VECTOR (Double X, Double Y, Double Z);
Static Bool Operator == (const vector ^, const vector ^);
Static Vector ^ Operator / (const vector ^, double);
Static Vector ^ Operator (const vector ^, const vector ^);
Static Vector ^ Operator - (const vector ^, const vector ^);
}
int main ()
{
Vector ^ Pa = GCNew Vector (0.231, 2.4745, 0.023),
Vector ^ PB = GCNew Vector (1.475, 4.8916, -1.23);
Vector ^ pc1 = pa pb;
Vector ^ PC2 = PA-PC1;
VECTOR ^ PC3 = PC1 / PC2-> x ();
IF (PC1 == P2)
// ...
}
3.6 conversion operator
Talking about unpleasant feelings, in the V1 language design, it has to be written in the op_implicit function to specify that a conversion feels is not like C . For example, this is a MYDOUBLE class definition from the V1 language specification:
__gc struct myduble
{
Static myduble * op_implicit (INT i);
Static int appet_explicit (mydouble * val);
STATIC STRING * OP_EXPLICIT (MYDOUBLE * VAL);
}
That is to say, given an integer, forced conversion this integer becoming the algorithm for MyDouble is implemented by the OP_Implicit operator. Further, this conversion will be implicitly executed by the compiler. Similarly, give a MyDouble object, two OP_ExPlicit operators are algorithms for forced converting objects to a integer or a hosted string entity. However, the compiler does not perform this conversion unless the user explicitly requires.
In C #, it looks like this:
Class myduble
{
Public Static Implicit Operator MYDOUBLE (INT I);
Public Static Explicit Operator INT (MYDOUBLE VAL);
Public Static Explicit Operator String (MyDouble Val);
}
If the explicit public access sign each member seems to be quirky, the C # code looks more like C than the c managed extension. So we have to fix this problem. But how can we do it?
On the one hand, the C programmer translates a single parameter constructor inside the expression into a conversion operator. However, on the other hand, this design has proven to be so difficult to handle, so that ISO-C committee introduces a keyword explicit, just to deal with its unexpected consequences, for example, an Array class with a integer variable as dimensional parameters The integer variable is implicitly converted to an Array object, even when this is the most un needed case. Andykoenig is the first person that caused me to pay attention to this problem. At that time, he explained a design habit, and the second parameter in the constructor is only used to stop this bad thing. So I don't regret the implicit conversion of the lack of single-parameter constructors in C / CLI. (Translator Note: KOENIG LOOKUP supported in Visual Studio .NET 2003 in the C grammar is named by Andy Koenig's name).
On the other hand, when you design a class in C , a conversion is never a good idea. The best example of this is the standard String class. Implicit conversion is a single parameter constructor with a string of a C style. However, it does not provide a corresponding implicit conversion operator to convert a String object to a C style string - but requires a user to explicitly call a named function - in this example, it is c_str ().
Thus, the association of the implicit / explicit behavior of the conversion operator (and a collection of such a conversion to the unified declaration) seems to be a C to the original support of the conversion operator, which supports since 1988 Robert Murray released the headline of Usenix C as a speech of Building Well-Behaved Type Relationships IN C , which has become an open warning world, and the speech eventually leads to an Explicit keyword. The revised V2 language supports the support of the conversion operator looks like this, which is slightly simpler than C #, because the default behavior of the operator supports implicit conversion algorithm applications:
Ref struct myduble
{
PUBLIC:
Static Operator MYDOUBLE ^ (INT I);
Static Explicit Operator INT (MYDOUBLE ^ VAL);
Static Explicit Operator String ^ (MyDouble ^ VAL);
}
Another change in V1 to V2 is that a single parameter constructor in V2 is derived by declaring explicit. This means that an explicit conversion is required in order to trigger its call. But note that if an explicit operator has defined, it is called instead of a single parameter constructor.
3.7 Explicit Overloading of Interface Members
There is often a need to provide an example of two interface members in the class that implements the interface - an object to operate by an interface handle and another is used to operate objects through a class interface. E.g:
Public __gc class r: public iCloneable
{
// Use iCloneable ...
Object * icloneable :: clone ();
// Use a R object ...
R * clone ();
}
In V1, we solve this problem by providing an explicit interface member statement named interface name and class name. The method of class interface operation is not named interface name. In this example, when clone () is explicitly invoked by an instance of R, this can be eliminated from the type of the return value.
In V2, a universal overload mechanism is introduced to replace the previous syntax. Our example will be rewritten as follows:
Public Ref Class R: Public iCloneable
{
// Use iCloneable ...
Object ^ interfaceclone () = iCloneable ::clone;
// Use a R object ...
Virtual r ^ clone () New;
}
This amendment needs to give a method of a member of the overloaded interface a unique name in the class. Here I provide a relatively clumsy name InterfaceClone (). The amendment behavior is still the same - triggered the interfaceclone () that we renamed through the Icloneable interface, and the second clone () instance is called by the R type object.
3.8 Private virtual functions
In V1, the access level of the virtual function does not affect whether it can be overloaded in the derived class. This was modified in V2. In V2, the virtual function that is not accessible in the base class cannot be overloaded. E.g:
__GC Class My
{
// Cannot be visited in derived class ...
Virtual void g ();
}
__gc class file: public my {
PUBLIC:
/ Ok: In V1, g () overloads my :: g ()
// Error: In V2, it cannot be overloaded: my :: g () cannot be accessed ...
Void g ();
}
For this design, there is actually no corresponding in V2. To overload this function, you must simply change the members of the base class to access - that is, non-private. The inheritance method does not have to take the same access level. In this example, the minimum change is the statement of the members of my MY to protect the access level. This method is still disabled by My access.
Ref class my {
protected:
Virtual void g ();
}
Ref class file: my {
PUBLIC:
Void g ();
}
Note Under V2, if the base class lacks an explicit Virtual keyword, a warning will be generated.
3.9 The connection method of static constants is no longer Literal
Although Static Const integration members are still supported, their connection mode properties are modified. The previous connection properties are now done through a Literal integration member. For example, consider the following V1 class:
PUBLIC __GC CLASS Constants {
PUBLIC:
Static const INT log_debug = 4;
// ...
}
It produces the underlying CIL attribute of this domain (pay attention to the literal properties of the black body):
.field public static literal INT32
Modopt ([Microsoft.visualc] Microsoft.Visualc.isconstmodifier) Standard_client_prx = int32 (0x00000004)
Although it is still compiled under V2 syntax
Public ref class constants {
PUBLIC:
Static const INT log_debug = 4;
// ...
}
But no longer a Literal property, so it is not recognized as a constant by the CLI runtime library.
.field public static int32 modopt ([Microsoft.visualc] Microsoft.visualc.isconstmodifier) Standard_client_prx = int32 (0x00000004)
In order to have the same intermediate language Literal property, the statement should be changed to the use of the newly supported Literal data, as shown below:
Public ref class constants {
PUBLIC:
LITERAL INT log_debug = 4;
// ...
}
4 value type and its behavior
In this section, we look at the CLI enumeration type and value class type, and review the boxes and access to the box examples on the CLI, and consider internal and constraints. The language varies greatly in this area. 4.1 CLI enumeration type
There is a __value keyword in front of the original language CLI enumeration statement. The intent here is to distinguish between traditional enumerations and derived CLI enumerations from System :: ValueType, suggesting that they have the same function. E.g,
__Value Enum E1 {Fail, Pass};
PUBLIC __VALUE ENUM E2: UNSIGNED SHORT {
NOT_OK = 1024,
Maybe, ok = 2048
}
The revised language is used to resolve this distinction between the traditional enumeration and CLI enumeration. Similarly, the __ value keyword was discarded, replaced with a pair of segment key enum classes. This implements the keyword pair of keywords in the statement of the reference, value classes, and interface classes. (Translator Note: Write program actually rumored? ^ _ ^ Bb)
ENUM CLASS EC;
Value Class VC;
REF CLASS RC;
Interface class IC;
The enumeration in the revised language design looks like the translation of E1 and E2 as follows:
ENUM CLASS E1 {Fail, Pass};
Public ENUM CLASS E2: UNSIGNED SHORT {
NOT_OK = 1024,
Maybe, ok = 2048
}
In addition to this small sentence modification, the behavior of hosted enumeration types has changed in many ways:
The pre-declaration of the CLI enumeration is no longer supported in V2. There is no such correspondence in V2. This will only cause errors when compiling. __Value enum status; // v1: Ok
Enum class status; // v2: Error
The order of overloading parsing between the internal construction calculative type and the object type is reversed in V2! A side effect is that the hosted enumeration cannot be repeatedly implicitly converted to an arithmetic type as in V1. Unlike in V1, in V2, the hosting enumeration has its own namespace. In V1, enumeration members are visible in the range of enumerations. In V2, enumeration members are defined within the range of enumerations.
4.1.1 CLI enumeration is a type
For example, consider the following code snippet
__Value enum status {fail, pass};
Void f (Object *) {cout << "f (object) / n";
Void f (int) {cout << "f (int) / n";}
int main ()
{
STATUS RSLT;
// ...
f (RSLT); // Which f is invoked?
}
For traditional C programmers, the natural answer to this problem is that the reloaded instance of the called F () is F (int). An enumeration is a constant symbol constant and is converted as a standard integer in this example. In fact, in the original language design, this is essentially the result of this call resolution. This leads to some surprises - not when we use it with traditional C framework ideas, but when we need them and existing BCL (Base libraries), this enumeration is an indirectly derived from Object. In the revised language design, the called F () instance is F (Object ^).
V2 Select Mandatory Not Support Implicit Conversion between CLI enumerations and arithmetic types. This means that any assignment from the hosted enumeration object to the arithmetic type requires an explicit forced conversion. For example, suppose
Void f (int);
Is an unloading method, in V1, call
f (RSLT); // ok: v1; error: V2
It is feasible, the value in the RSLT is implicitly converted into a constant value. In V2, the compilation of this call failed. To translate it correctly, we need to insert a forced conversion operator: f (SAFE_CAST
4.1.2 Limited domain of CLI enumeration type
One of the changes between C and C languages is that C adds a qualifier to the structure. In C, the structure is just a collection of data, neither supports the interface and does not support the associated defined domain. This is a controversial problem at the time when it is a radical change, and a new C user who is transferred from C language. The relationship between the traditional and CLI enumerations is similar.
In the original language design, an enumeration element that has been tried to manage the enumeration is a weak insertion name for simulating the unlimited domain characteristics of the traditional enumeration. This attempt has proven to be failed, the problem is that this caused the name of the enumeration dollar to overflow to the global namespace, causing the management name conflict. In a revised language, we support the defined domain of managed enumeration in other CLI languages.
This means that the use of any unqualified CLI enumerations will not be recognized by the revised language. Let's take a look at an actual example.
// Original language design supports weak injection
__gc class xdcmake {
PUBLIC:
__Value enum _recognizerenum {
Undefined,
Option_USAGE,
XDC0001_ERR_PATH_DOES_NOT_EXIST = 1,
XDC0002_ERR_CANNNOT_WRITE_TO = 2,
XDC0003_ERR_INCLUDE_TAGS_NOT_SUPPORTED = 3,
XDC0004_WRN_XML_LOAD_FAILURE = 4,
XDC0006_WRN_NONEXISTENT_FILES = 6,
}
ListDictionary * optionList;
ListDictionary * itaglist;
XDCMAKE ()
{
OptionList = new listDictionary;
// Here Are The Problems ...
OPTIONLIST-> Add (s "?", __box (option_usage)); // (1)
OptionList-> Add (s "help", __box (option_usage)); // (2)
Itaglist = new listDictionary;
Itaglist-> add (s "returns",
__box (xDC0004_WRN_XML_LOAD_FAILURE)); // (3)
}
}
The use of three unlimated enumeration metadits ((1), (2), and (3)) will need to be limited in the translation of the revised language to allow the source code to be compiled. Here is the correct translation of the original code:
Ref class xdcmake
{
PUBLIC:
ENUM CLASS _RECognizerenum
{
Undefined, Option_USAGE,
XDC0001_ERR_PATH_DOES_NOT_EXIST = 1,
XDC0002_ERR_CANNNOT_WRITE_TO = 2,
XDC0003_ERR_INCLUDE_TAGS_NOT_SUPPORTED = 3,
XDC0004_WRN_XML_LOAD_FAILURE = 4,
XDC0006_WRN_NONEXISTENT_FILES = 6
}
ListDictionary ^ OptionList;
ListDictionary ^ itaglist;
XDCMAKE ()
{
OptionList = gcnew listDictionary;
OptionList-> add ("?", _ recognizerenum :: option_usage); // (1) optionlist-> add ("help", _ recognizerenum :: option_usage); // (2)
Itaglist = gcnew listDictionary;
Itaglist-> Add ("Returns",
Recognizernum :: xdc0004_wrn_xml_load_failure); // (3)
}
}
This changed the design strategy between traditional and CLI enumerations. Because the enumeration of the CLI maintains an associated defined domain in V2, the package enumeration in a class is no longer necessary and effective. This usage continues to develop with the CFRONT 2.0 of the Bell Lab, but also to solve the global name pollution.
In the BEERRY SCHREAM library made in the Bell Lab, Jerry does not have all relevant enumerations defined in the package, and universal enumeration dollars, such as read, write, append, so that users are almost impossible to compile. Their existing code. A solution is to replace these names, such as IO_READ, IO_WRITE, and more. The second solution is to modify the language to add a qualified domain, but it is impossible at the time. (A compromised solution is to encapsulate enumerations in class or nested classes, and the name of the enumeration and its enumeration dollar exists in a defined domain around.) In other words, put an enumeration in the class Motivation - At least the original motive - is not theoretically, but a practical solution for a global naming problem.
For the enumeration of V2 CLI, the enumeration is forced to encapsulate no benefits in the class. In fact, if you look at the SYSTEM namespace, you will see that enumeration, classes, and interfaces exist in the same namespace.
4.2 Implicit Packing
OK, we have a good voice. This will lose a election in the political field. In language design, this means we impart a theoretical location in the field of practical experience, and it is actually an error. A similar situation is that in the original multi-inheritance language design, Stroustrup decides that it cannot initialize a virtual base sub-object in the derived constructor, so that the C language requires any classes as a virtual base class must define a default structure. function. This will only be monitored by subsequent virtual dispensers by the default constructor.
The problem with the false base class is to push the role of initializing the shared belly object to each subsequent derived class. For example, I define a base class, which needs to be assigned a buffer, and the size of the buffer specified by the user is transmitted as a parameter of the constructor. If I implemented two subsequent virtual success, the name is inputb and Outputb, each need to pass a constructor of the base class a specific value. Now I define the IN_OUT class derived from INPUTB and OUTPUTB, then two base class sub-object values that should be passed to the shared are not effective.
Therefore, in the original language design, Stroustrup disables the explicit initialization of the dawn class member for the member initialization class table of the derived class constructor. Although this solves this problem, it is actually unable to control the virtual base class, which proves to be incapable. The National Health Institute of Keith Gorlen, a free version of the free version of the NiHCl SmallTalk Collection Library is to advise Bjarne, let him consider one of the main people in a more flexible language design.
One of the principles of an object-oriented hierarchy is that a derived class should only involve its own non-private member of its own and direct base class. In order to support a flexible virtual success design, Bjarne has to destroy this principle. The bottom level in the level is responsible for initializing all the virtual objects, regardless of which level is in the level. For example, both InputB and Outputb have a responsibility to explicitly initialize their relief base classes. When the IN_OUT class is derived from the InputB and Outputb, IN_OUT starts to have the responsibility to perform once the removed virtual base class initialization, and explicit initialization in INPUTB and OUTPUTB is suppressed. This provides the flexibility required by language developers, but is at the expense of semantics. If we define the virtual base class must have no status, and only allows you to specify an interface, then this burden is removed. This is a recommended design in C . In C / CLI, this is the policy of the interface type.
This is a code instance, do something simple - explicit packing here is largely useless.
/ / The original language design requires explicit packing operations
INT my1dintarray __gc [] = {1, 2, 3, 4, 5};
Object * myobjarray __gc [] = {
__box (26), __box (27), __box (28), __BOX (29), __box (30)
}
Console :: WriteLine ("{0} / t {1} / t {2}", __box (0),
__box (my1dintarray-> getlowerbound (0)),
__box (my1dintarray-> getupperbound (0)));
You can understand, there will be a lot of packing. In V2, the package of the value type is implicit:
// Revised language hidden packing
Array
Array
Console :: WriteLine ("{0} / t {1} / t {2}", 0,
My1DINTARRAY-> getLowerBound (0),
My1DINTARRAY-> GetupperBound (0));
4.3 Tracking handle to the value of the box
Packing is a feature of the CLI type unified system. The value type directly contains its status, and the reference type is a dual meaning: the entity of the name is a handle, which points to a unnamed object assigned on the hosted stack. For example, any value type to an object initialization or assignment requires a value type in the CLI heap - this is the location of the box - first assigning the relevant memory, then replicates the value of the value, and finally returning this anonymous The combination of values / references. In this way, the following code is written in C #.
Object o = 1024; // c # implicit box
The simplicity of the code makes the boxes close to transparency. C # design not only hides the hood (Translator Note: Discussion on the translation of Under the hood can be found at http://blog.joycode.com/zhanbos/archive/2004/06/10/24208.aspx, here It is the complexity of the operation of direct translation, and also hides the complexity of the packing itself. On the other hand, V1 takes into account that it may result in a decrease in efficiency, so it directly requires the user to explicitly declare:
Object * o = __box (1024); // v1 Explicit box
It is like other options here. In this case, in this case, forced users to conduct explicit requests, just like a person's mother is constantly embarrassed when he is going out. Now we will take care of yourself, don't you? On the one hand, based on some reasons, a person is not concerned about the foreign newspaper, which is called maturity. On the other hand, based on some reasons, a person must trust the maturity of the child. Change the mother to the language designer, programmer replaced with children, this is the reason why V2 is contained in the box. Object ^ o = 1024; // v2 implicit box
__box keyword is a second important service in the original language design, which is not in C # and Microsoft Visual Basic .NET: it supports two methods of literal and tracking handles to directly operate an instance of a hosted stack . For example, consider the following applet:
int main ()
{
Double result = 3.14159;
__box double * by = __box (result);
Result = 2.7;
* Br = 2.17;
Object * o = br;
Console :: WriteLine (s "Result :: {0}", result.toString ());
Console :: WriteLine (s "Result :: {0}", __box (result));
Console :: WriteLine (s "Result :: {0}", Br);
}
WriteLine calls the generated underlayer display shows the price of the value of the value after the box (thanks Yves DOLCE points out these differences), where the row of the black body shows the relevant focus of each call.
// Console :: WriteLine (S "Result :: {0}", result.toString ());
LDSTR "Result :: {0}"
LDLoca.s Result
Call instance string [mscorlib] system.double :: toString ()
Call void [mscorlib] system.console :: WriteLine (String, Object)
// Console :: WriteLine (S "Result :: {0}", __box (result));
LDSTR "Result :: {0}"
LDLOC.0
Box [mscorlib] system.double
Call void [mscorlib] system.console :: WriteLine (String, Object)
// Console :: WriteLine (s "Result :: {0}", BR);
LDSTR "Result :: {0}"
LDLOC.0
Call void [mscorlib] system.console :: WriteLine (String, Object)
Direct delivery of the boxed value to console :: Writelin avoids the trigger of the box and toString () (of course, this is to initialize the BR using the previously mentioned bag box, so unless BR is truly used, otherwise We will not really gain something.
In a revised language design, it also has become more elegant and more integrated into the type of system while maintaining the value of the packaged value type. As an example, here is the above apparent translation:
int main ()
{
Double Result = 3.14159; double ^ br = result;
Result = 2.7;
* Br = 2.17;
Object ^ o = br;
Console :: WriteLine (s "Result :: {0}", result.toString ());
Console :: WriteLine (s "Result :: {0}", result);
Console :: WriteLine (s "Result :: {0}", Br);
}
4.4 Value Types
Here is the usage of a normal value type of a specification in the V1 language specification:
__Value struct v {INT I;
__GC struct r {v vr;};
In V1, we can have four types of syntax variants (the semantics of 2 and 3 here):
V v = {0};
V * pv = 0;
V __gc * pvgc = 0; // Format (2) is the implicit format of (3)
__box v * pvbx = 0; // must be partial
4.4.1 The virtual method of triggering inheritance
Format (1) is a standard value object. And it is quite easy to understand unless someone tries to trigger a inheritance method, such as toString (). E.g,
v.toString (); // error!
To trigger this method, because it is not overloaded in V, the compiler must access the relevant virtual functions of the base class. Because the value type is locally stored, there is no pointer to the virtual function table (VPTR), so this requires V to be packaged. In the original language settings, implicit packing is not supported, and must be explicitly declared by programmers as follows:
__box (v) -> toString (); // v1: Note this arrow
The main motivation behind this design is educational - it hopes to display the behind-the-scenes underlying mechanism to programmaking, so that he can understand the cost of providing function instances in the value type. If V contains a TOSTRING instance, the box is unnecessary.
The buddy section of the box is displayed, not the cost of the packing itself, is removed in the revised language design.
v.tostring (); // v2
But the price is that the misleading designer thinks that the price of the explicit toString instance is not provided in V is also exempt from. The reason for choosing an implicit packing is because only one class designer and countless user, they don't have to modify V to avoid the freedom of modifying the troubled explicit packing.
Deciding whether to provide a heavy-duty toString in the value type depends on its frequency and location. If it is rarely called, then this definition is very good. Similarly, if it is in the non-performance sensitive area of the program, add it will not increase the regular performance of the application. Alternatively, a tracking handle of a packaged value can be retained, and it will not need to replace the box through it.
4.4.2 Value Category No longer a constructor
Another difference between the original and revisit language designs of the value type is to cancel the support for the default constructor. This is because the CLI may create a value type object without calling the relevant default constructor. In other words, the support of the default constructor of a value type in V1 is actually not guaranteed. Due to lack of guarantees, it feels that this support is better than the uncertainty in its application.
This is not as bad as the first look. This is because each value type object is automatically cleared (each type will be initialized to its default value) that is, the member of the local instance will not be undefined. In this sense, the ability to define a normal constructor is actually not a loss - and in fact more efficient in the CLI execution.
The problem occurs when the original V1 language user defines a explicit constructor. It is not corresponding to the revised V2 language. The code in the constructor will need to be moved to an initialization method with name, and this method needs to be explicitly called by the user. In addition, the declaration of value type objects in the revised V2 language has not changed. Its disadvantage is that the value type cannot encapsulate traditional types, which is as follows:
The value type does not support the destructor. In other words, a series of actions are automatically triggered at the end of the object life. Traditional classes can only be included in a hosted class as a pointer, and then distribute space on conventional piles.
We may like to use value types to pack a traditional class to avoid two heap assignments: traditional heap allocation traditional classes, CLI stack assignment managed packaging classes. Packing a traditional class in the value type allows you to avoid allocation of the hosted stack, but there is no way to automatically recover the memory allocated on the traditional pile. The reference type is the only feasible type of managed type for packaging traditional classes.
4.4.3 Internal Pointer
Format (2) and (3) can point to anything in this or next area (that is, anything on the hosted and traditional piles). Therefore, for example, the following formats in the original language design are permitted:
// from 4.4
__Value struct v {INT I;
__GC struct r {v vr;};
V v = {0};
V * pv = 0;
V __gc * pvgc = 0; // Format (2) is the implicit format of (3)
__box v * pvbx = 0; // must be partial
R * R;
PV = & v; / / point to a value type on the stack
PV = __nogc new v; // Point to a value type on the conventional pile
PV = pvgc; // We don't sure what location pointing
PV = pvbx; // Point a value type that is contained in the conventional pile
PV = & r-> vr; // Pointing internal pointers in a reference type on a conventional pile
Thus, a V * can point to the address on the local block (so it can be a wild pointer); in the global range, it can point to the address of a conventional heap (it can also become a wild pointer, for example, the object it pointing is already When deleted); point to a tracking handle of a CLI (and therefore tracking the internal member of the reference object on the garbage collection mechanism) and the number of reference objects on the CLI pile (like the name, the internal pointer also transparently tracks the object Mobile).
In the original language design, the traditional aspect of V * cannot be separated. That is, its processing contains processing that points to the possibility of an object or child object on a conventional heap.
In a revised language design, the value type pointer is divided into two parts: V *, the location is limited to non-CLI heap, and internal pointer interior_ptr
/ / Do not point to the address of the hosted stack
V * pv = 0;
/ / Can but do not have to point to the address other than the traditional pile
Interior_Ptr
Format (2) and (3) in the original language correspond to Interior_Ptr
V ^ pvbx = nullptr; // __box v * pvbx = 0;
The following statements in the original language design correspond to the internal pointer in the revised language design (they are value types in the system namespace).
INT32 * PI; => interior_ptr
Boolean * pb; => interior_ptr
The built-in type is not considered a managed type, although they do act as an alias for the type in the system namespace. Therefore, the corresponding original and revised languages are correct:
When translating V * in your existing program, the most conservative strategy is always converting into interior_ptr
4.4.4 Constraint Pointer
Garbage collection machines may move on the pluck at the CLI pile, which usually occurs during the heap compression. (Translator Note: The feeling of moistening, Win32's programmer is definitely true) This move is not a problem for tracking handles, tracking references, and internal pointers because these entities are transparent. However, if the user passes the address of the object of the CLI heap to the running time library environment, in this case, this unstable movement is easily caused by the runtime library failed. To avoid such objects, we must pin them in their external domains.
In the original language design, a constraint pointer is declared using __pin to define a pointer to declare. Here is an example of a small number of modifications on the basis of the original language specification:
__GC struct h {Int j;};
int main ()
{
H * h = new h;
INT __PIN * K = & H -> J;
// ...
}
In a new language design, a constraint pointer is a grammar as a syntax similar to the internal pointer.
Ref struct h
{
PUBLIC:
Int J;
}
int main ()
{
H ^ h = gcnew h;
PIN_PTR
// ...
}
The constraint pointer under the revised language is a special case of an internal pointer. The V1 is still retained on the restriction of the constraint pointer. For example, it cannot be used as a return type of parameters or methods, and it can only be declared as a partial object. However, some additional limits are added to the revised language design:
The default value of the constraint pointer is NullPtr, not 0. PIN_PTR <> cannot be initialized or assigned 0. The assignment of 0 in the current code will need to be modified to use Nullptr. The constraint pointer under V1 allows the entire object, as described in the example of the original language specification as described below:
__GC struct h {Int j;};
Void f (g * g)
{
H __pin * ph = new h;
g-> incr (& pH -> j);
}
In the revised language, the entire object returned by the NEW expression is not supported. Specifically, it is necessary to pin the address of the internal member. for example:
Void f (g ^ g)
{
H ^ ph = gcnew h;
PIN_PTR
g-> incr (pj);
}
(Translator Note: For a hosted array, you can pin one of the elements.)
5. Summary of language changes
A revision described in this section is a language miscepted. This section contains a revision of a string constant, a revision of the omitted number and parameter properties, revised from TypeOf to TypeID, and a new forced conversion marker SAFE_cast. 5.1 String Region
In the original language design, the hosted string constant is indicated by the prefix. for example:
String * ps1 = "hello";
String * ps2 = s "goodbye";
The performance difference between the two initialization is not small, like the CIL seen by ILDASM:
// String * ps1 = "hello";
LDSFLDA VALUETYPE $ ARRAYTYPE $ 0xD6117DD
Modopt ([Microsoft.visualc] Microsoft.visualc.isconstmodifier)
'? A0xbdde7aca.unnamed-global-0'
NEWOBJ Instance Void [Mscorlib] System.String ::. Ctor (int8 *)
STLOC.0
// String * ps2 = s "goodbye";
LDSTR "Goodbye"
STLOC.0
Remember (or learning) adding S with S and S will have a significant performance saving before the string constant. In a revised V2 language, the processing of the string is transparent, and the context of the instance is determined. S no longer needs to be specified.
Which interpretation we need to tell the compiler? In this case, we use explicit conversions. E.g:
f (SAFE_CAST
Further, string constants now match a conversion to normal string instead of first matching a standard conversion, although this does not affect, but this changed the use of string and const char * as the overload function set of different parameters Analytic mode. An analysis of an instance of the Const Char * is now marked as ambiguity. for example:
Void f (const char *);
Void f (string ^);
// v1: f (const char *);
// v2: Error: Ambiguity ...
f ("ABC");
What happened here? Why is there a difference here? Because there is a function example in which one name is f, it is necessary to apply a function overload parsing mechanism to call the function. Formal function overload parses include the following three steps:
Collect the candidate function. The candidate function is a name function of the function of matching calls on the scope. For example, because my () is called by an instance of R, all the names of all the names MY but not any base class and the base class system are not a candidate function. In our example, there are two candidate functions. Two member function names in R are called MY. If the candidate function set is empty, then the call failed. Feasible functions in the candidate function. A feasible function is a function that triggered a given number and type of a given number and type of types of calls. If the feasible function set is empty, the call failed. Select the most matching function. This is achieved by the conversion scheduled level of parameters to optional function parameters. It is relatively simple for a one-dimensional function; but the multi-functional function is a bit complicated. If there is no best match, the call failed. In other words, if the level of the necessary conversion from the actual parameter to the formal parameters is the same, the call is marked as ambiguity.
In the original language design, the parses of this call triggers the const char * instance as the best match. In V2, from "ABC" to const char * and String ^, the necessary conversion is now equivalent - in other words, the same level - therefore call the flag is bad - that is, Ambiguity. This leads us two questions:
What is the type of actual parameter "abc"? What is the algorithm for judging a conversion than another priority?
The type of string constant "ABC" is const char [4] - remember that there is an implicit NULL terminator at the end of each string constant.
It is judged that a conversion contains a possible type conversion than another preferred algorithm. Here is my understanding of this list - of course, all of these conversions are implicit. Use explicit conversion tags to redefine the ranking, just like parentheses redefine the order of operations.
A precise match is the best. Amazing is that an actual parameter does not have to fully match the formal parameter type to perform precise matching, only need to be close enough. This is the key to understanding the principles and languages of this example and how language modifications. The improvement takes precedence over standard conversion. For example, short int to int enhances priority to INT to Double transformation. Standard conversion is preferred in the packing conversion. For example, int to Double transformation takes precedence over INT to OBJECT packing. The packing conversion takes precedence over implicit user-defined conversions. For example, INT to Object's packing takes precedence over the conversion operator to the smallint value class. Implicit user-defined conversions take precedence over all conversation. Implicit User Custom Conversion is the last outlet before an error (Warning: The formal parameter may contain a parameter array or the omitted location).
In this case, why does it accurately match do not necessarily identify a match? For example, the const char [4] type is not fully conforming to const char * or string ^, but our examples of the ambiguity is still between two exact matchs!
When the exact match occurs, a series of small conversions are included. There are 4 spotting conversions in ISO-C still satisfy accurate match, three of which are called left value (translator Note: Right to left value, array to pointer and function to function pointer). The fourth conversion is referred to as limiting conversion (translator Note: increasing qualifiers, such as const, or volatile). Three left value conversions are identified as more prioritized matching than qualified conversions.
Our left value conversion form is a conventional array to the transformation of the pointer. This is what happened in const char [4] to const char *. Therefore, it is an exact match from MY ("ABC") to MY (const char *). In the early versions of our C / CLI language, this is actually optimal conversion.
Because the compiler wants to mark the call to have ambiguous, this requires a transition from const char [4] to string ^, also a precise match. This is the revision after introducing V2, and calls that are marked as ambiguity
5.2 argument arrays and omitted numbers
Explicit support for parameter arrays supported by C # and Visual Basic .NET in the original language design and the upcoming V2 language in Visual Studio 2005. Alternatively, the normal array is marked with an attribute as follows:
Void Trace1 (String * Format, [Paramarray] Object * args []);
Void TRACAT (STRING * FORMAT, Object * args []);
Although this seems to be the same, the parameter array property tags it is an array that varies when the number of parameters in the C # or other CLI language. The procedures for program behavior between the original and revised languages are parsing of the overload function set, just like the example provided below, an instance declares an omitted number, another declares array attribute: int my (...) ; // 1
INTMY ([paramarray] int32 []); // 2
In the original language design, the resolution of the omitted number is positioned before the attribute, because the attribute is not a formal part of the language. In V2, the argument array is now supported by the language, so it takes precedence from the omitted number because it is a stronger type. Therefore, in the original language, call
MY (1, 2);
Analysis to MY (...), and in the revised language, it resolves to the parameter array instance. If your application's behavior relies on the omitted number priority to the call of the parameter array, you need to modify the declaration of the function or call the function of the function.
5.3 TypeOf is changed to T :: TypeID
In the original language design, the __ typeof () operator returns the related type * object when transmitting a managed type name, for example:
// Create and initialize a new Array instance.
Array * myintArray =
Array :: CreateInstance (__typeof (int32), 5);
In the revised language design, __ typeof is replaced by an additional typeid, which returns a Type ^ when specifying a managed type.
// Create and initialize a new Array instance.
Array ^ MyIntarray =
Array :: CreateInstance (Int32 :: TypeId, 5);
5.4 Introduction to Qualification Conversion and SAFE_CAST <>
Note that this is a lengthy section in a sense, so some fast-resistant people can quickly jump to the end to read the actual changes.
Modifying an existing structure is very difficult - in some occasions more difficult than writing the original structure; less freedom, and the solution tends to be ideal and actually dependent on existing structures compromise. For example, if you have already placed books, you will know that the existing information is restricted to reformatting this page; you can't allow text to overflow to the back page, so you can't add or remove too much Content, and often feel corrected to suit the layout.
Language extensions are another example. Back to the early 1990s, object-oriented programming has become an important model, and the demand for the type of C type security is gradually increased. Type forced conversion is the user's forced conversion from the base class pointer or reference to the derived class pointer or reference. Type forced conversion requires an explicit conversion because if the base class pointer is not a pointer to the class type, the program is likely to do some very bad things. The problem is that the actual type of the base class pointer is one of the work of the runtime library, so the compiler cannot check it. Alternatively, or in other words, type type conversion is like a virtual function call, requiring some form of dynamic analysis. This has two problems:
Why is the type forced conversion in the object-oriented model? Is the virtual function mechanism not suitable for all? That is to say, why can't I claim any type of forced conversion (or any type of conversion) is the programmer's design failed? Why does I support type forced conversion to a problem with C ? After all, this is not a problem in any object-oriented language such as SmallTalk (or subsequent Java and C #)? Why is it difficult to add support for type forced conversion in C ?
A virtual function represents a type of algorithm in a type of family (I didn't consider including the interface, which is not supported in ISO-C , but available in C / CLI, and represents an interesting alternative design Program). The typical representative of this type of family is a class hierarchy with a virtual base class that declares a general interface (virtual function), and a bunch of specific derived classes that represent the actual types of families in the application domain. For example, a lightweight hierarchy in a computer imaging (CGI) application domain has a common attribute such as Color, Intensity, Position, ON, OFF, etc. A few beams can be sprinkled in an image model, and they can be controlled by a general interface without worrying that the light is concentrated, parallel light, and the whole direction (considering the sun), or the light of the flap can be used. In this case, the type is forcibly converted to a particular type to call its virtual interface unnecessary, because all call mode is the same, so it is not wise. However, in an actual environment, the situation is not always the same; in many cases, consider the speed; the programmer may choose the type of method to call the required method, if so, internal direct execution will replace the virtual function Mechanism execution.
Therefore, one reason for the type forced conversion in C is to suppress the virtual function mechanism to obtain considerable runtime performance (Note Automation This manual optimization is the active area of research, but this is more difficult than replacing the display of Register or Inline keywords. ).
The second cause of the type forced conversion is critical to the nature of the polymorphism. One point of polymorphism is to divide it into passive and dynamic in two forms.
A virtual function (and type forced conversion function) calls a dynamic use of polymorphism: Perform an actual type of operation in the program execution.
However, assigning a derived class object to its base class pointer, is a passive form of polymorphism, where polymorphism is used as a transparent mechanism. The polymorphism is the main purpose of the Object class, for example, in the popular CLI. As the passive form is used, the base class pointer for transmitting and storage typically provides an over-abstract interface. For example, Object only provides five methods through its interface; any more specific behavior requires an explicit type forced conversion. For example, if we want to adjust the angle or irradiation angle of the spotlight, we need an explicit type forced conversion. The virtual interface in the sub-type family cannot be a supercoming of all possible methods of all its members, so the type forced conversion function in the object-oriented language is always necessary.
If a secure type forced conversion feature is necessary in an object language, why is C spend this long time to add it? The problem is how to make the type information of the runtime pointer. For the virtual function, just like most people, the runtime information is the compiler. Two parts: (a) class object contains an additional virtual function table pointer member (at the beginning of the class object This is an interesting history that itself is pointing to the appropriate virtual function table - so, for example, a virtual function table pointer member of a spotlight point points to a concentrated object virtual function table, parallel light Virtualistic table, etc .; and (b) each virtual function has a related fixed position in the table, and the actually called the address stored in the table. Thus, for example, the false preframe function ~ Light may be associated with No. 0, and Color is No. 1, and the like. This is an unfunctory but effective strategy because it is set to compile and represent the minimum price.
The current problem is how to enable the pointer to access the type information without changing the size of the object pointing to the C pointer, or you may add some type encoding directly by adding the second address. This is impossible to be accepted by those who choose to be object-oriented-oriented - they still have a big impact in the user community - (and applications) accepted. An additional possibility is to introduce a specific pointer to the polymorphism, but this will be terrible confusion, and make the mixing two very difficult - especially in the direction of the pointer algorithm. Maintenance Associate a runtime table for each pointer to the current type, and dynamically updated it is also unacceptable. The problem is now a different but reasonable programming expectation of two user communities. Solutions need to compromise between two communities, not only allowing the expectations of each community and also allows interoperability capabilities to be implemented. This means that the programs provided by the two communities seem not to be complete, and the final implementation solution is likely to be in perfect in both. The actual solution is parsed around the definition of the polymorphism: the polymorphic class is a class containing virtual functions. A polymorphic class supports the type forced transformation of dynamic type security. This solves the problem of "maintenance pointer in address" because all polymorphisms contain additional pointer members, point to their related virtual functions. Therefore, related types of information can be saved in an extension virtual function table structure. Type Safety Type Forced Conversion The price of (almost) limits the range of users.
The next problem with the type of mandatory conversion of type security is its syntax. Because it is a forced conversion, ISO-C is the use of unfilled forced conversion syntax, so write the following sample code:
Spot = (Spotlight *) PLight;
But this is rejected by the committee because this does not allow users to control the cost of enforcement. If the dynamic type security type is forced to convert the previous unsafe but is a static tag, then it becomes an alternative, and the user cannot suppress the cost of running at its unnecessary and possible price.
Typically, C has a total mechanism to suppress the functions supported by the compiler. For example, we can close the virtual function mechanism by using the class limit domain operator Box :: Rotate (Angle) or through the class object instead of this class or the censored virtual function - back a suppression is unnecessary but it is Some implementations ... It is similar to constructing a temporary object when declaring as follows:
// The compiler can freely optimize this temporary object ...
X x = x :: x (10);
Therefore, it is proposed to be re-considered, and many alternative symbols have been considered, and finally submit it to the committee (? Type) form, indicating that it is uncertain - the dynamic-essent. This gives the ability to switch between two forms - static or dynamic -, but no one is satisfied. So it returns to the chart board. The third, also a successful mark is the current standard Dynamic_cast
In ISO-C , Dynamic_CAST returns 0 when applied to an inappropriate pointer type, and throws an STD :: BAD_CAST exception when applying to a reference type. In the original language design, use Dynamic_cast to a managed reference type (because its pointer expression method) always returns 0. __TRY_CAST
PUBLIC __GC Class ItemVerb;
Public __gc class itemverbcollection
{
PUBLIC:
Itemverb * EnSureverBarray () []
{
Return __TRY_CAST
(verblist-> toarray (__ typeof (itemverb *)));
}
In the revised language, __ try_cast is rewritten as Safe_cast. Here is the same code snippet in the revised language:
Using namespace stdcli :: language;
Public Ref class itemverb;
Public Ref Class Itemverbcollection
{
PUBLIC:
Array
{
Return Safe_cast
(Verblist-> toarray (itemverb :: typeid);
}
}
In the hosted area, the ability to limit programmers to make code becomes unrecognizable. The ability to convert the verifiable code is important. This is a key part of the dynamic programming model represented by C / CLI. For this reason, the forced type conversion instance of the old style is internally rewritten as a runtime, so that way, for example:
// Internal converted to the above equivalent SAFE_CAST expression
(Array
On the other hand, since the polymorphism provides two modes of dynamics and passive, it is sometimes necessary to perform a type forced conversion, just to obtain the accessibility of the sub-type non-virtual application programming interface. For example, this can occur in a member of any type of class in the hierarchy (using passive polymorphism as a transparent mechanism), but is known when the actual instance in a particular program context. In this case, the system programmer feels that the performance cost of a runtime type check is unacceptable. If the CLI is a managed system programming language, it must provide some methods to allow some compile (still static) to force conversion down. This is why the use of Static_cast tags in the revised language still allows for a reasons why it is maintained as a compile time type.
// Good: Forced conversion is executed when compiling
/ / There is no correctness check when running
STATIC_CAST
Verblist-> toArray (itemverb :: typeid);
Of course, the problem is that Static_CAST that the programmer is not guaranteed is correct and good. In other words, the verification of the hosted code cannot be guaranteed. This is a more urgent consideration than the dynamic programming model of the traditional environment, but it is not enough to make a system programming language to disable the ability to switch static and dynamic types forced conversion.
There is a performance trap and defect in C / CLI to note that there is no difference in the performance of the old style forced conversion markers and new style Static_cast tags in traditional programming. However, in a new language design, the performance cost of the old style is more expensive than the new style Static_cast marker because the compiler is converted to the old style forced conversion tag to a throwing anomalous runtime check. Further, it changed the execution profiling of the code because it causes an unpuckled exception in the program - may be smart, but if you use static_cast, then the same error will not result in an exception. Some people may have objection, good, which will help those "nails" users transfer to new style markers. But only when it fails; otherwise it only causes the user who uses the old-style marker to run slowly without understanding, just like the shortcomings of the C programmer below:
// Defect # 1:
// Initialization can be exempted from the creation of a temporary object, and the value is not
Matrix M;
m = another_matrix;
// Miss # 2: Object declarations away from it
Matrix M (2000, 2000), N (2000, 2000); if (! Mumble) return;
Appendix: Promoting the revised language design
The most significant and compelling changes between the original and revised language designs are a statement of a managed reference type object:
// Original language
Object * Obj = 0;
// Revised language
Object ^ obj = nullptr;
When people see it, two major issues are: Why hats (^ symbols) are well-known in Microsoft's corridors, but more fundamentally, why do you want new syntax? Why is the original language design that cannot be reorganized to reduce aggressive to the old code, unfair, unfamiliar revised Edition C / CLI language design?
C is based on a machine-oriented system view. Although it supports a high-level type system, there is always a mechanism to avoid it, which always leads to the dependence of the machine. When the situation is serious, and the user works to do some incredible things, they will bypass the abstraction process of the application, re-separating the type as the address and offset.
The CLI is a software abstraction layer between the operating system and our application. When the situation is serious, the user will reflect on the enforcement of the environment, query, code, and objects without the basis of the basis, skipping instead of following type systems, but this experience will be a mess for people who are accustomed to the earth.
For example, what do we mean when we write down below?
T t;
Ok, in ISO-C , no matter what T is, we can confirm the following characteristics: (1) Compile time is the memory allocation size equal to the number of SIZEOF (T) by the number of bytes; (2) In the application In the scope of the program in the program, this T associated with memory is independent of all other objects; (3) The memory directly maintains T-related state / value; and (4) memory and status exists in the scope of T.
What is the result of the following characteristics?
(1) Tell us that t can't be polymorphic. That is, it does not represent a series of types in an integrated level. In other words, a polymorphic type cannot have a compile time memory allocation unless the derived class does not assign an additional memory. This is true when T is a simple type or a base class of a complex level.
A polymorphic type in C may only be limited to a pointer (T *) or reference (T &) - that is, if the declaration is just indirectly reference a T object. If I write down
Base b = * new deerid;
Then b does not point to a DeriveD object located on the traditional pile. The value B does not associate with the Derived object assigned by the New Expression, and the DeriveD object base section is truncated and copied to a stand-alone-based instance B. This does not have a corresponding description in the CLI object model.
In order to submit resources to delay to runtime, C explicitly supports two indirect forms:
Pointer: t * pt = 0;
Quote: T & RT = * Pt;
The pointer is consistent with the C object model. in
T * pt = 0;
In the middle, PT directly maintains a value of a SIZE_T type, with a fixed size and scope. Syntax words are accustomed to switching between direct use of pointers and indirectly using pointed objects. What model is applied to what / when applied / how to apply this issue, is a redemptic dismissal: * Pt .
The reference provides a simple syntax of the complexity of the surface pointers. Also maintain its efficiency.
Matrix Operator (Const Matrix &, Const Matrix &);
Matrix M3 = M1 M2;
The reference is not switched between direct and indirect patterns; but the time difference between the two: (a) they are initialized; but (b) is transparent in all subsequent uses, they are transparent. In a sense, the reference represents a singular quantum that represents C object model physics: (a) takes up space, but in addition to the temporary object; (b) use deep copy when assigning values (DEEP Copy) Use a shallow copy when initialization; and (c) unlike const objects, the parameters actually have no body. Although they don't have much use in ISO-C , there is no much use in the function parameters, but in the single foot of the language revision, they become inspirational rotations.
C . NET design challenge
On the literal, each of the functions of C extensions for supporting the CLI, the problem is always attributed to "How do we integrate this (or that) this (or that) this (or that) to C , make it (a ) Let C programmers feel nature, and (b) feel like a first-class CLI own feature "? . Based on these considerations, this name is not implemented in the original language design.
Reader's language design challenge
So in order to see some steps, here we face the challenge: How do we declare and use a CLI reference type? It is a significant difference between it and the C object model: different memory models (garbage collection), different replica semantics (shallow copies), different inheritance models (integrated, based on Object, single inheritance plus support for interfaces).
Original C hosted expansion design
The basic design choice for supporting the CLI reference type in C is to reserve existing languages when the decision; or expand the language, thus breaking the existing standard.
What would you do? Each choice will be criticized. Conditions Conditions for a person who believes that additional language supports representing the field abstraction (considering parallel and threads) or modes change (considering object-oriented type-subtype relationships and generics).
If you believe that additional language support is just a representative of another field, you will choose to keep the existing language. If you understand that the additional language support represents a change in a programming model, you will extend your language.
Briefly, the original language design thinks that additional language support is just an abstraction of another field - it is clumsy called hostess extension - therefore logically subsequent design selection is to maintain an existing language.
Once we are committed to maintaining the existing language, only three alternative methods are actually possible - remember, I discuss us on simple "how to express a CLI reference type":
Let language support transparency. The compiler determines the semantics according to the context. An error is generated when there is ambiguity, and the user will determine the meaning of the context through some special syntax (can consider the multi-beam of the priority function.). Add a domain abstract support in a library (considering the use of standard template STL as possible models) to retrieve certain existing language elements, based on the description of the accompanying specifications, limits their allowable uses and behaviors. Consider the initialization and type forced conversion of the virtual base class, or the use of multiple STITIC keywords, such as functions, within the file range, and class declarations).
The preferred choice for everyone is the first item. "It's just like other things in the language, just a little difference. Let the compiler are judged.". A large success here is to transparent to the user in a way as the existing code. Take your existing app, add one or two objects, compile, then TA-DAH, it will be completed. It is easy to use. It can be used in terms of type and source code. No one will argue that this program is not an ideal plan, which is largely like no one argues to argue the ideality of the permanent. In physics, the obstacle of this problem is the second law of thermodynamics, as well as the existence of entropy. In a multi-field programming language, the rules are significantly different, but the ideal system's disintegration is as clear. In a multi-field programming language, things are working in their respective models, but when the model is incorrect, it tends to crash, causing the program crashes or worse, running but generates errors. This is the most common in support independent object-based object-oriented class programming. Slice makes each C novice programming confusion:
DeriveDClass DC; // an object
Baseclass & bc = dc; // Good: BC is really a DC
BaseClass BC2 = DC; // Good: But DC may be truncated to adapt to BC2
Therefore, the second law of language design, in the case, it is to make things different things look sufficient to remind users, avoid, um, a mess in his or her programmed. We are accustomed to using a half-hour or two hours of introduction to start the first step of understanding the differences between pointers and references, and a large number of programmers still cannot clearly describe when using reference declarations, what Use the pointer, and the reason.
These confused can not be more difficult to programmatically, and there is always a trade-off between their ability to simply remove them and support them. And they differ in the transparency of the design, and whether they are practical. When the pointer to the member is introduced into the language, the member selection operator is extended (eg, from-> to-> *), and the pointer to the function is similar (from int (* pf) () to Int (X :: * pf) ()). Similarly, initialization of static members of the class is also expanded, and so on.
The reference is support for the operator overload. You can get an intuitive syntax
Matrix C = A B; // Matrix Operator (Matrix LHS, Matrix RHS);
C = A B C;
But it is difficult to say is an effective implementation. C language pointer alternative - this provides performance - separated by its non-Deroven grammar:
// Matrix Operator (Const Matrix * LHS, Const Matrix * RHS);
Matrix C = & A & B;
C = & (& A & B) & C;
The introduction reference provides the efficiency of the pointer, but the simple semantics of value type access is retained. Its statement is similar to the pointer and is easy to understand.
// Matrix Operator (Const Matrix & LHS, Const Matrix & RHS);
Matrix C = A B;
But for programmers who are accustomed to using pointers, its semantic behavior is proven to be confused.
This is what is easy to understand and correctly use the managed reference type for C programmers who habits the static behavior of C objects, how is it? And it is naturally, what is the best design of this effort to help programmers? We feel that the difference between the two types is sufficiently, so that in order to ensure separate processing, we exclude option # 1. Even in a revised language, we still support this option. Those who argue this choice - once included most of us - just understanding this problem without sitting down. This is not a blame, just a truth. Therefore, if you consider the front design challenge and propose a transparent design, then I will conclude, according to our experience, it is not a viable solution, I insist on this.
The second and third options, or take a library design, or reuse the existing language elements, it is feasible, and each has the strength, because Stroustrup's CFront source code is easy, so in Bell Laboratory Solutions are tired. It has been to a certain extent, which is popular (HCE). A modified CFRONT to add parallelism, and B modify CFRONT to add their favorite domain extensions, everyone shows off their new C language modification version, and Stroustrup's correct answer is that this is best in a library.
In this case, why don't we choose a library solution? Well, part of the reason is just a feeling of feelings. As we feel that there are two types of differences, it is much better, so that we don't have many similar places in the same type, so that we must be treated similarly. A library type is in many ways, like a built-in type in a language, but it is actually not. It is not a one-level language element. We feel that we must try our best to make the reference type a first-class element, so we choose not to deploy a library solution. This option still has controversy.
In this way, the transparent solution is abandoned in order to collect the type and the existing type object model too much, and the library solution is abandoned in order to quote the type and the existing type object model, we will abandon the library solution. How to integrate the reference type to the existing language.
If we start from the blueprint, we can certainly do anything we want to provide a unified type system, and - at least before we have modified this type of system - anything we do has a new decoration of the brilliant decoration . This is what we have done in the manufacturing and ordinary crafts. However, we are limited, this is a blessing. We can't abandon the existing C object model, so anything we do must compatible with it. In the original language design, we limit our own, do not introduce any new tags; therefore; we must use the tags we have already. This did not leave much flexibility to us.
Therefore, in order to cut the focus, in the original language design, it is given to the limit listed (it does not have too much confusion). The language designer feel that the only feasible managed reference type is a reuse of the current pointer syntax - quote is not It is flexible because they can't be re-assigned, and they can't do not quote any object:
// All objects are allocated on the hosted stack ...
Object * pobj = new object;
/ / The standard String class allocated on traditional piles ...
String * pstr = new string;
of course. These pointers are significantly different. For example, when the entity of the object pointing to POBJ moves during the compression process of the hosted stack, POBJ will be transparently updated. In traditional C , there is no connection between the relationship between POBJ and its pointed outsystem. Traditional C pointer concept is not an articon address and an indirect object reference. A reference type handle encapsulates the actual virtual address of the object to achieve runtime garbage collection; in addition to the consequences of destroying this package in the garbage collection environment, this largely like private data members packaged Implementation to achieve scalability and localization. Therefore, although Pobj looks like a pointer, many of the common features of the pointers are disabled, such as mandatory type conversions other than pointer counts and type systems. If we use a fully qualified syntax to life and assign a reference, we can make this difference more significant:
// Ok, now this looks different ...
Object __gc * pobj = __GC new object;
String * pstr = new string;
It seems that it seems to be aware of the pointer solution. After all, it looks like a Natural goal of a New expression, and both support a shallow copy. One problem is that the pointer is not a type of abstraction, but a representation in a machine (and an explanation of how to explain the range of memory after the first byte and the type tag of the internal organization), and this does not match the software time to memory Abstraction, and therefore lack safety. This is a historical problem between the object model of different models.
The second question is (metaphor warning: a uncomfortable metaphor is about to be tried - all people in stomach is recommended to pause reading or jump to the next paragraph) an inevitable drawback of a closed language design is to be forced to reuse Simple and significantly different concepts, leading to the spirit of programmers in the desert, the metaphors warning.)
Reproduction of the pointer syntax caused programmers' cognitive chaos: you have to distinguish too much traditional and hosted pointers, and this will interfere with code writing, it is best to manage it with a advanced abstraction. In other words, as a system programmer, we sometimes need to show a certain performance, but we won't stay at this level.
The success of the original language design is to compile the existing C programs, and provide only a small amount of work to publish the package mode of the existing interface in a new hosted environment. Additional functions can also be added in the hosted environment, and depending on practices and experience, one person can directly transplant this part of the existing application or that part to the hosted environment. This is a magnificent achievement on a C programmer with existing code and the foundation. We don't need to be embarrassed for this.
However, there is a significant weakness in the grammar and insight of the original language design. This is not the shortcomings of the designer, but the conservative nature of the basic design - continuing to stay in the existing language. This is from misunderstanding of hosting support, which is a domain abstraction, not a revolutionary programming model, requires a language extension similar to Stroustrup to simultaneously support object-oriented and normal programming. This is the revised language design representative, and it is necessary and reasonable reason, even if it causes some sadness of the original language designer. This is a common motive behind this guide and the translation tool.
Revision C / CLI language design
Once it is clearly supported by the public language foundation in C , a separate programming model is clear, and the language must be expanded to provide a first-class programming experience to the user, and an elegant design integration with ISO-C Pay attention to the feelings of the C community, and win their loyalty and assistance. Also, the original name of the original language design, the c hosted extension must also be replaced.
The flagship in the characteristics of the public language is a reference type, and its integration in the existing C language represents a certificate of concept. What is the general standard? We need a way to represent managed types, separating it, still feel similar to the existing type system. This allows people to recognize that this normal form category is familiar but noticed its only feature. The similarity is the reference type introduced by Stroustrup in the original invention of C . So this is normal form as: type typemodtoken id [= init];
Here TypeModToken is one of the symbols reused in the new context environment (also similar to the introduction of references) in the language.
This is initially surprisingly controversial and is still the pain of some users. I remember the beginning of the two most common responses (a) I can use a TypedEf to handle (don't looked blinking), and (b) this is really not bad (the latter reminds me, my reply is to use left Move and right shift operators to input and output in the iostream library).
The necessary behavior features that it demonstrates the semantics of the object when operator operation object, which is a point that the original syntax cannot support. I like to call it flexible reference, thinking about it and existing C references (yes, the use of two references here - one is the hosted reference type, the other is "This is not a pointer (don't looked blinking)" here Traditional C type - is regrettable, very like a design pattern of the Template in the design pattern of the Gang of Four Patterns.):
It must not reference any object. Of course, traditional quotes cannot be done directly, although people always tell me how to initialize a reference to a 0 type forced conversion. (Conventional practice is to provide an explicit monomer that represents a converter of Null objects, which often uses the default parameters of a function parameter) which may not need an initial value, but can do not quote any one in the life of the life period. Object. It can be assigned to another object. The default assignment or initialization of another instance is shallow value.
Just like a person makes me realize that I consider this cute puppy from the opposite direction. That is, I define it by distinguishing it and the traditional reference nature, rather than using it as a handle of a managed reference type.
We want to call this type of handle rather than a pointer or reference, because these two terms have traditional aspects. The handle is a more suitable name, because it is a packaged mode - a person named John Carolan first introduces it to me, with a cute nominal: Cherish Cat (Cheshire Cat, Creating Lewis Carroll) Alice is roaming, refer to http://en.wikipedia.org/wiki/cheshire_cat, because the body of the object being operated can disappear in the case of you unaware.
(Translator Note: CHESHIRE CAT represents a design pattern: a special case described as "bridge" mode in "design mode" in Gamma et al.: A class of private implementation, and a public class, Has a pointer to a private class instance, and forwarding all member functions to a private class instance.)
In this case, this disappointing event is derived from a repositioning of potential reference types in a cleaning of the garbage collection machine. In fact, this repositioning is transparently tracked by the running moment, and the handle is updated to a new correct position. This is why it is called a tracking handle.
Therefore, I finally select the operator about the members I want to mention on the new tracking reference grammar. For me, it seems that I don't want to use an object syntax (.). Others think that the pointing syntax (->) is also obvious, and we discussed in multiple aspects of tracking references: // If you want to use people who want to use pointer syntax
T ^ P = gcnew t;
// If you want to use people who want to use object syntax
T ^ c = a b;
In this way, like the light inside the physics, a tracking reference is in some programs like an object, in other programs context, like a pointer. Finally, the members who put into use are the arrow, just like in the original language design.
Summary of keywords on keywords
Finally, an interesting question is why Stroustrup adds Class to C language design? It is actually not necessary to introduce it because the C language structure in C is expanded, and anything that Class can do. I have not asked Bjarne, so I didn't have a special knowledge at this point, but this is an interesting question, and it seems to give some equivalent to add some keywords in C / CLI.
A possible answer - I call it a Foot Soldier Shuffle - is an argument: No, the introduction of CLASS is absolutely necessary. After all, there is not only the difference between the two keywords, but also the level of derived access is different, so why can't we be two?
But slower, introducing a new keyword is not only incompatible with the existing language, but also introduces a different branch of the language (Simula-68), there will be the risk of an angry C language community. This motivation is really a difference between the default access level? I can not confirm.
On the one hand, the language does not stop when the class designer uses the Class keyword and makes all implementations. The language itself has no common and privately accessed strategy, so it is difficult to see the reason why the unclear default access level license is valued - in other words, it is important than the cost of incompatibility.
Similarly, it seems that there is a problem in design practices in design practice. This is more complicated and more difficult to understand because it does not show behavior of class / subclass, and therefore destroy alternative. It represents a reuse that is not an interface, and I believe that the private inherit is an error.
Of course, I can't open this, because in the language market, a person never recognizes a little problem of the product, because this provides ammunition to the enemy who quickly seizes any advantages to seize the market quota. It is particularly prevalent in the small circle of intellectuals. Alternatively, a person is acknowledged when a person is ready until a new improved product is ready to spread.
What other reasons may be made in introducing CLASS this incompatibility? The architecture of the C language is an abstract data type. C language class concept (of course, this is not from C ) is data abstraction, and the idea of encapsulation and interface agreed. Data types are abstracted in life and behavior entities. This is for education importance, because the words will make language differences - at least one in one by one. This is another lesson of revised design remembrance.
Why does C do not completely remove the structure? Keep one and introduce another one is not elegant, and the same is minimized the difference between them. But have you have other options? The structural keyword has to be retained because C has to keep down and compatible with C; otherwise, not only it will be more unwelcome in the existing programmer, but may not be allowed (but this is another Time, another story of another location) Why is the access level of the structure is public? Because if not, the existing C program does not compile. This will be a disaster in practice, although the programmer is likely to have heard it in Advanced Principles of Language Design. There may be a mandatory process in the language, enforce a policy: use structure to ensure a public implementation, in turn, use classes to ensure a private implementation and public interface, but this policy is not derived from practice, so it will be a bit corrected .
In fact, in the release test of the CFRONT 1.0 language compiler of the Bell Lab, there is a small argument between language lawyers: pre-declaration and subsequent definition (or any such combination) must continue to use this or other key Word, or can be used in exchange for use. Of course, if there is any true importance of the structure, this debate will not happen.
Thank you
I want to thank the Visual C team here, they constantly help and guide me to understand the problem of transplantation from C hosted to revised C / CLI language designs. Thank arjun bijanki and Artur Laksberg, they have two tolerated my very confusion. Thanks to Brandon Bray, Jonathan Caves, Siva Challa, Tanveer Gani, Mark Hall, Mahesh Hariharan, Jeff peil, Andy Rich, Alvin Chardon, and Herb Sutter. They all offer incredible help and feedback. This article is the song of their professional opinions.
Related books
STL Tutorial and Reference Guide, David Musser, Gillmer Derge and Atul Saini, Addison-Wesley Book Company, 2001
C Standard Library, Nicolai Josuttis, Addison-Wesley Book Company, 1999
C Primer, Stanley Lippman and Josee Lajoie, Addison-Wesley Book Company, 1998
About author
Stanley Lippman is a Microsoft's Visual C team of architects, working together in C fields together in Bell Laboratory and inventors in 1984. It is also a special animation company that has worked in Watris Disney and DreamWorks, as well as software technology guidance of the film "Madworthy" (Fantasia 2000).