Classes and Structs (Managed) Definitions
To declare a managed class or struct, the new C++ syntaxes are shown below. |
class_access ref class name modifier : inherit_access base_type {};
class_access ref struct name modifier : inherit_access base_type {};
class_access value class name modifier : inherit_access base_type {};
class_access value struct name modifier : inherit_access base_type {};
Parameter | Description |
base_type (optional) | A base type. A ref class or ref struct can inherit from zero or more managed interfaces and zero or one ref types. A value class or value struct can only inherit from zero or more managed interfaces. |
class_access (optional) | The accessibility of the class or struct outside the assembly. Possible values are public and private. private is the default. Nested classes or structs cannot have a class_access specifier. |
inherit_access (optional) | The accessibility of base_type. The only permitted accessibility for a base type is public (public is the default). |
modifier (optional) | abstract and sealed are valid modifiers. |
name | The name of the class or struct. |
Example | ref class MyClass {...}; ref class MyClass2 : public MyClass::MyInterface {...}; public value struct MyStruct {...}; |
Table 1 |
Like their native equivalents, default member accessibility of a ref class or value class is private and default member accessibility of a ref struct or value struct is public. A value type cannot act as a base type. When a reference type inherits from another reference type, virtual functions in the base class must explicitly be overridden (with override) or hidden (with new (new slot in vtable)). The derived class functions must also be explicitly marked as virtual. You can detect at compile time if a type is a CLR type with __is_ref_class(type), __is_value_class(type), or __is_simple_value_class(type). In the development environment, you can get F1 help on these keywords by highlighting the keyword, (ref class, for example) and pressing F1. Try and study the following program example.
A Program Example
// Class.cpp : main project file.
#include "stdafx.h"
using namespace System;
ref class MyClass {
public:
int i;
// nested class
ref class MyClass2 {
public:
int i;
};
// nested interface
interface struct MyInterface { void f(); };
};
ref class MyClass2 : public MyClass::MyInterface {
public:
virtual void f() { Console::WriteLine("Testing MyClass2..."); }
};
public value struct MyStruct {
void f() { Console::WriteLine("Testing MyStruct..."); }
};
int main()
{
// instantiate ref type on garbage-collected heap
MyClass ^ p_MyClass = gcnew MyClass;
p_MyClass -> i = 4;
Console::WriteLine("p_MyClass -> i = {0}", p_MyClass -> i);
// instantiate value type on garbage-collected heap
MyStruct ^ p_MyStruct = gcnew MyStruct;
p_MyStruct -> f();
// instantiate value type on the stack
MyStruct p_MyStruct2;
p_MyStruct2.f();
// instantiate nested ref type on garbage-collected heap
MyClass::MyClass2 ^ p_MyClass2 = gcnew MyClass::MyClass2;
p_MyClass2 -> i = 5;
Console::WriteLine("p_MyClass2 -> i = {0}", p_MyClass2 -> i);
}
Output:
Quick Reference
To | Do this |
Create a class. | Use the keyword class. |
Control the visibility of variables and methods. | Use the access control keywords public, private, or protected, followed by a colon (:). |
Declare a reference type class. | Place the ref keyword before the class specifier. |
Declare a value type class. | Place the value keyword before the class specifier. |
Instantiate a reference type class object. | Declare a pointer of the class type for the object. Use the gcnew keyword followed by the name of the class to create the object on the CLR heap, and assign the returned pointer to the object pointer declared above. For example:
HeapClass ^ pObject1; pObject1 = gcnew HeapClass;
|
Instantiate a value type class object. | Define the object by stating its class followed by the variable name for the object. For example:
ValueClass object1;
|
Table 2 |
Supplementary Notes: Some Confusing Story - Managed Extensions of C++ vs new Visual C++ .Net syntax
The following section is about the Managed Extension for C++ and a new C++ syntax. It is intended for whom that already familiar with the Managed Extension for C++ used in Visual C++ 2002 and Visual C++ 2003. If you start fresh with Visual C++ 2005 or new C++, the new related keywords, operators etc. for new C++ that used in this Tutorial also explained and mostly with program examples.
The ^ Operator (Handle to Object on Managed Heap)
The ^ (caret or hat) operator declares a handle to an object on the managed heap. A handle to an object on the managed heap points to the "whole" object, and not to a member of the object. In Visual C++ 2002 and Visual C++ 2003, __gc * was used to declare an object on the managed heap. The ^ replaces __gc * in the new syntax. The common language runtime maintains a separate heap on which it implements a precise, asynchronous, compacting garbage collection scheme. To work correctly, it must track all storage locations that can point into this heap at runtime. ^ provides a handle through which the garbage collector can track a reference to an object on the managed heap, thereby being able to update it whenever that object is moved. Because regular C++ pointers (*) and references (&) cannot be tracked precisely, a handle-to object declarator is used. Member selection through a handle (^) uses the pointer-to-member operator (->).
Program Example
This sample shows how to create an instance of reference type on the managed heap. This sample also shows that you can initialize one handle with another, resulting in two references to same object on managed, garbage-collected heap. Notice that assigning nullptr to one handle does not mark the object for garbage collection.
#include "stdafx.h"
using namespace System;
// Th ^ handle
// compile with: /clr
ref class MyClass
{
public:
// Constructor
MyClass() : i(){}
// member variable
int i;
// Member function/method...
void Test()
{
i++;
System::Console::WriteLine("In Test()");
System::Console::WriteLine("i = {0}", i);
}
};
int main()
{
// instantiate object...
MyClass ^ p_MyClass = gcnew MyClass;
// First time call..
p_MyClass->Test();
MyClass ^ p_MyClass2;
p_MyClass2 = p_MyClass;
p_MyClass = nullptr;
// Second time call the method Test...
p_MyClass2->Test();
return 0;
}
Output:
The following sample shows how to declare a handle to an object on the managed heap, where the type of object is a boxed value type. The sample also shows how to get the value type from the boxed object.
|
This sample shows that the common C++ idiom of using a void* pointer to point to an arbitrary object is replaced by Object^, which can hold a handle to any reference class. It also shows that all types, such as arrays and delegates, can be converted to an object handle.
// Another ^ handle example
// compile with: /clr
#include "stdafx.h"
using namespace System;
using namespace System::Collections;
public delegate void MyDel();
ref class MyClass
{
public:
void Test() {Console::WriteLine("Test() method. In MyClass class.");}
};
void Test(Object ^ xob)
{
Console::WriteLine("In Test(). Type is {0}", xob->GetType());
}
int main()
{
// handle to Object can hold any ref type
Object ^ h_MyClass = gcnew MyClass;
// Instantiate new object...
ArrayList ^ arr = gcnew ArrayList();
arr->Add(gcnew MyClass);
// Converting to Object ^...
h_MyClass = dynamic_cast<MyClass ^>(arr[0]);
// Call...
Test(arr);
Int32 ^ bi = 1;
// Call...
Test(bi);
// Another new object instantiation...
MyClass ^ h_MyClass2 = gcnew MyClass;
// Another new object instantiation...
MyDel^ DelInst = gcnew MyDel(h_MyClass2, &MyClass::Test);
// Call...
Test(DelInst);
return 0;
}
Output: