The declaration of properties and operators has been extensively
reworked from Managed Extensions for C++ to Visual C++ 2008, hiding
the underlying implementation details that were exposed in the
Managed Extensions design. Event declarations have been modified as
well.
Under the category of changes that have no Managed Extensions
support, static constructors can now be defined out-of-line (they
were required to be defined inline within Managed Extensions), and
the notion of a delegating constructor has been introduced.
In
This Section
Property Declaration
Discusses changes to property declarations.
Property Index Declaration
Discusses changes to indexed property declarations.
Delegates and Events
Discusses changes to the syntax for declaring
delegates and events.
Sealing a Virtual Function
Discusses changes to the syntax for sealing a
function.
Overloaded Operators
Discusses changes to operator overloading.
Changes to Conversion Operators
Discusses changes to conversion operators.
Explicit Override of an Interface Member
Discusses changes to the method for explicitly
overriding an interface member.
Private Virtual Functions
Discusses changes in the way private virtual
functions are handled in derived classes.
Static Const Int Linkage Is No Longer Literal
Discusses changes in the way
static const integral members are linked and how
to explicitly declare a constant using the new
literal keyword.
Property Declaration
The way to declare a property in a managed class has changed from
Managed Extensions for C++ to Visual C++ 2008.
In the Managed
Extensions design, each set or
get property accessor is specified as an
independent method. The declaration of each method is prefixed with
the __property keyword. The method name
begins with either set_ or
get_ followed by the actual name of the
property (as visible to the user). Thus, a Vector
providing an x coordinate
get property would name it
get_x and the user would invoke it as
x. This naming convention and separate
specification of methods actually reflects the underlying runtime
implementation of the property. For example, here is our
Vector with a set of coordinate
properties:
This spreads out the functionality associated with a property and
requires the user to lexically unify the associated sets and gets.
Moreover, it is verbose. In the new syntax, which is more like that
of C#, the property keyword is followed
by the type of the property and its unadorned name. The
set and get
access methods are placed within a block following the property
name. Note that unlike C#, the signature of the access method is
specified. For example, here is the code example above translated
into the new syntax.
Copy Code
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 methods of the property reflect distinct access
levels � such as a publicget and a private
or protectedset,
an explicit access label can be specified. By default, the access
level of the property reflects that of the enclosing access level.
For example, in the above definition of Vector,
both the get and set
methods are public. To make the
set method protected
or private, the definition would be
revised as follows:
Copy Code
public ref class Vector sealed {
public:
property double x {
double get() {
return _x;
}
private:
void set( double newx ) {
_x = newx;
}
} // note: extent of private culminates here �
// note: dot is a public method of Vector
double dot( const Vector^ wv );
// etc.
};
The scope of an access keyword within a property extends until
either the closing brace of the property or the specification of an
additional access keyword. It does not extend beyond the definition
of the property to the enclosing access level within which the
property is defined. In the above declaration, for example,
Vector::dot() is a public method.
Writing the set/get properties for the three
Vector coordinates involves three steps:
declare a private state member of the appropriate type.
return it when the user wishes to get its value.
assign it the new value.
In the new syntax, a shorthand property syntax is available which
automates this usage pattern:
The interesting side effect of the shorthand property syntax is
that although the backstage state member is generated by the
compiler, it is not accessible within the class except through the
set/get accessors.
Property Index Declaration
The syntax for declaring an indexed property has changed from
Managed Extensions for C++ to Visual C++ 2008.
The two primary
shortcoming of the Managed Extensions language support of indexed
properties is the inability to provide class-level subscripting;
that is, all indexed properties are required to be given a name, and
thus there is no way, for example, to provide a managed subscript
operator that can be directly applied to a Vector
or Matrix class object. A second less
significant shortcoming is that it is visually difficult to
distinguish a property from an indexed property � the number of
parameters is the only indication. Finally, indexed properties
suffer from the same problems as those of non-indexed properties �
the accessors are not treated as an atomic unit, but separated into
individual methods. For example:
Copy Code
public __gc class Vector;
public __gc class Matrix {
float mat[,];
public:
__property void set_Item( int r, int c, float value);
__property float get_Item( int r, int c );
__property void set_Row( int r, Vector* value );
__property Vector* get_Row( int r );
};
As you can see here, the indexers are distinguished only by the
additional parameters to specify a two or single dimension index. In
the new syntax, the indexers are distinguished by the bracket ([,])
following the name of the indexer and indicating the number and type
of each index:
Copy Code
public ref class Vector {};
public ref class Matrix {
private:
array^ mat;
public:
property float Item [int,int] {
float get( int r, int c );
void set( int r, int c, float value );
}
property Vector^ Row [int] {
Vector^ get( int r );
void set( int r, Vector^ value );
}
};
To indicate a class level indexer that can be applied directly to
objects of the class in the new syntax, the
default keyword is reused to substitute for an explicit name.
For example:
Copy Code
public ref class Matrix {
private:
array^ mat;
public:
// ok: class level indexer now
//
// Matrix mat �
// mat[ 0, 0 ] = 1;
//
// invokes the set accessor of the default indexer �
property float default [int,int] {
float get( int r, int c );
void set( int r, int c, float value );
}
property Vector^ Row [int] {
Vector^ get( int r );
void set( int r, Vector^ value );
}
};
In the new syntax, when the default indexed property is
specified, the two following names are reserved:
get_Item and set_Item. This is
because these are the underlying names generated for the default
indexed property.
Note that there is no simple index syntax analogous to the simple
property syntax.
Delegates and Events
The way to declare delegates and events has changed from Managed
Extensions for C++ to Visual C++ 2008.
The double underscore is no
longer needed, as shown in the following sample. Here a sample code
in Managed Extensions:
Events (and delegates) are reference types, which is clear in the
new syntax because of the use of the hat (^).
Events support both an explicit declaration syntax and the trivial
form shown in the preceding code. In the explicit form, the user
specifies the add,
raise, and remove methods
associated with the event. (Only the add
and remove methods are required; the
raise method is optional.)
Under Managed Extensions, if you provide these methods, you do
not also provide an explicit event declaration, but you must decide
on a name for the event that is not present. Each method is
specified in the form add_EventName,
raise_EventName, and
remove_EventName, as in the following example taken from the
Managed Extensions specification:
Copy Code
// explicit implementations of add, remove, 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 new syntax simplifies the declaration, as the following
translation demonstrates. An event specifies the two or three
methods enclosed in a pair of braces and placed immediately after
the declaration of the event and its associated delegate type, as
shown here:
Copy Code
public delegate void f( int );
public ref struct E {
private:
f^ _E; // delegates are also reference types
public:
E() { // note the replacement of 0 with nullptr!
_E = nullptr;
}
// the new aggregate syntax of an explicit 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 );
}
};
Sealing a Virtual Function
The syntax for sealing a virtual function has
changed from Managed Extensions for C++ to Visual
C++ 2008.
The __sealed
keyword is used in Managed Extensions to modify
either a reference type, disallowing subsequent
derivation from it (see
Declaration of a Managed Class Type), or to
modify a virtual function, disallowing subsequent
overriding of the method in a derived class. For
example:
Copy Code
__gc class base { public: virtual void f(); };
__gc class derived : public base {
public:
__sealed void f();
};
In this example, derived::f()
overrides the base::f()
instance based on the exact match of the function
prototype. The __sealed
keyword indicates that a subsequent class inherited
from the derived class cannot provide an override of
derived::f().
In the new syntax, sealed
is placed after the signature rather than being
allowed to appear anywhere before the actual
function prototype, as was previously allowed. In
addition, the use of sealed
requires an explicit use of the
virtual keyword as
well. That is, the correct translation of
derived, above, is as
follows:
Copy Code
ref class derived: public base {
public:
virtual void f() override sealed;
};
The absence of the virtual
keyword in this instance results in an error. In the
new syntax, the contextual keyword
abstract can be used in
place of the =0 to
indicate a pure virtual function. This was not
supported within Managed Extensions. For example:
Copy Code
__gc class base { public: virtual void f()=0; };
can be rewritten as
Copy Code
ref class base { public: virtual void f() abstract; };
Overloaded Operators
Operator overloading has changed significantly from
Managed Extensions for C++ to Visual C++ 2008.
In
the declaration of a reference type, for example,
rather than using the native
operator+ syntax, you explicitly write out
the underlying internal name of the operator � in
this case, op_Addition. In
addition, the invocation of an operator has to be
explicitly invoked through that name, thus
precluding the two primary benefits of operator
overloading: (a) the intuitive syntax, and (b) the
ability to intermix new types with existing types.
For example:
In the new syntax, the usual expectations of a
native C++ programmer are restored, both in the
declaration and use of the static operators. Here is
the Vector class
translated into the new syntax: