A value type is a lightweight, c++/cli-class mechanism that is ideal for small data structures and, from a semantic standpoint, is similar to a value (value).
In contrast, instances of reference types-including those declared on the stack-are managed by the garbage collector, whereas instances of value types are not. In general, a better implementation of a value class should have only some data members, without the need for inheritance, so that there is no significant data overhead in function passing and return values, or assignment operations.
Value class First Impressions
Take a look at the point class in Example 1, which converts a reference class to a value class by replacing the ref with value, which is similar to a reference class (ref) and is a keyword that contains a space. As you can imagine, the only difference between a value class and a value structure (value struct) is that the default accessibility is private and the latter is public.
Example 1:
using namespace System;
Public value class Point
{
int x;
int y;
Public
To define a read-write instance of a property X and Y
property int X
{
int get () {return x;}
void set (int val) {x = val;}
}
property int Y
{
int get () {return y;}
void set (int val) {y = val;}
}
Defining an instance constructor
Point (int xor, int yor)
{
X = XOR;
Y = Yor;
}
void Move (int xor, int yor)
{
X = XOR;
Y = Yor;
}
virtual bool Equals (object^ obj) override
{
if (obj = = nullptr)
{
return false;
}
if (GetType () = = Obj->gettype ())
{
point^ p = static_cast<point^> (obj);
return (X = = p->x) && (Y = = p->y);
}
return false;
}
static bool operator== (Point P1, point p2)
{
Return (P1. X = = P2. X) && (P1. Y = = P2. Y);
}
static bool operator== (point% p1, point% p2)
// {
Return (P1. X = = P2. X) && (P1. Y = = P2. Y);
// }
static bool operator== (point& p1, point& p2)
// {
Return (P1. X = = P2. X) && (P1. Y = = P2. Y);
// }
virtual int GetHashCode () override
{
return X ^ (Y << 1);
}
Virtual string^ ToString () override
{
Return String::Concat ("(", X, ",", Y, ")");
}
};
A value class is automatically inherited from System::ValueType, while System::ValueType inherits from System::Object, but this cannot be explicitly declared. The value class implicitly indicates "sealed", that is, it cannot be used as a base class, and specifying a protected for its class members is meaningless and is not allowed. If you want to declare a value class explicitly (or reference a class), you can do this as follows:
value class X sealed {/*...*/};
Note that there is no default constructor here. For a value class, the CLI itself sets the bits of all the fields in the class instance to zero, so it cannot provide its own default constructor; However, 0, false, and nullptr may not be the appropriate default value for other types, so for some specific types, You need to replace the value type with a reference type. (Conforming to the C++/CLI implementation will represent false and nullptr as zero for all bits.) )
Another limitation of value classes is that they come with a default copy constructor and an assignment operator, both of which are bitwise copied and not overloaded.
If you want to implement the Equals function in the point class, it is simpler than in the reference class. Keep in mind that we are overloading the definition of this version in System::Object, and it accepts a object^ because this type of argument is likely to have a nullptr value, where you can omit to check whether to compare this step for yourself, and for the equals implementation of the reference class, This step is required because more than one handle can refer to the same object. But then again, in the current value class, an instance with no two values can represent the same instance, two identical value instances, representing only two point with the same coordinates, but modifying the x-coordinate of the one in which it does not affect the same value of the other one.
When an instance of point is passed to equals, the boxing occurs as a value type (which eventually inherits from System::Object)-that is, an instance of Object is allocated on the garbage collection heap, and it contains a copy of the passed in point. Because a new object is created, there is only one handle and no other point is the same.
the = = operator function, which accepts the point handle before, is now streamlined to a single line, and the point operator is replaced by the accept handle, and the-> that is used to select the member is substituted for the dot operator. Because the given value type is sealed, the value of the same type is the only match with the value type argument point. Similarly, there is no need to check nullptr to confirm whether to compare itself, nor does it need to check that the passed in object is of exactly the same type.
the = = operator function used to trace the reference is basically not required to change much, but it deletes the part that detects the same type. However, these two = = operator functions, it is best to keep only one, in order to avoid point1 = = Point2 call to cause ambiguity. (When declaring function arguments, you can also use the standard C + + reference character, rather than the%, because both can be interchanged between the local type and the value type.) However, because instances of this type do not exist in the garbage collection heap, they do not change their location during garbage collection and therefore do not need to be traced. )
Example 2 uses most of the members of a value class, most notably it contains an instance of the static point class, which is not possible in a reference class. In fact, not only can you not have a static instance of a reference class, you cannot even have a static handle of this type.
Example 2:
using namespace System;
Point p1;
static Point p2(3,4);
int main()
{
static Point p3(4,7);
Console::WriteLine("p2 is {0}", p2);
Point% p4 = p3;
Point p5 = p2;
p5 = p2;
Console::WriteLine("p1 == p2 is {0}", p1 == p2);
Console::WriteLine("p1.Equals(p2) is {0}", p1.Equals(p2));
}
p2 is (3,4)
p1 == p2 is False
p1.Equals(p2) is False
When Console::WriteLine is first invoked, it is passed in a value to a point, but the function expects to accept an object reference, where the point value is automatically boxed and the boxed object reference is passed to the function.
As you can see in the definition, P5 is initialized by the default copy constructor, and the next line of code, the default assignment operator, copies P2 to P5.