|
Let’s summarize what you’ve learned about classes so far. Classes are known as reference types because you always access objects by using reference variables. Consider the following line of code:
MyClass* pc = new MyClass();
In this example, pc is a reference variable that lets us refer to the MyClass object created by the new operator. Accessing objects using references in this way allows the .NET garbage-collection mechanism to reclaim the resources used by an object when nobody has a reference to it any more. This feature of .NET makes for efficient memory usage and means that you won’t suffer from one of the traditional problems of C++ programs—memory leaks.
The second thing you’ve learned about classes is that they consist of data members and member functions. Data members represent the state of the object, and it’s good practice to make them private to the class. Member functions provide the behavior of the object, and they use the data members to determine how to respond. All operations on objects are done by calling member functions, using the - > operator, as in the following line of code:
result = pc->DoOperation();
So how are value types different from reference types, and why do we need them? As the name value type implies, they have been designed to hold values, such as integers, floating-point numbers, Booleans, and characters. Anything that is basically a wrapper around a simple value—and is less than about 16 bytes in size—is a good candidate for a value type.
We need value types because we want simple values to be used as efficiently as possible, but we also want them to be usable as objects. Using values as objects is a problem with object-oriented languages because if basic types are represented as objects, all operations (such as addition and multiplication of integers) have to be done by calling functions, which isn’t efficient at all. On the other hand, if basic types are not represented as objects, operations on them can be very efficient but we can’t use them where objects are needed.
.NET gets around this problem with value types, which are represented and used as efficiently as built-in types, but which can also be used as objects when necessary. You don’t need to know this is happening most of the time. This process, called boxing, is discussed in Chapter 25.
The following table summarizes the value types provided by the .NET Framework.
Value Type | Description | Managed C++ Equivalent Type |
Byte | An 8-bit unsigned integer | char |
SByte | An 8-bit signed integer | signed char |
Int16 | A 16-bit signed integer | short |
Int32 | A 32-bit signed integer | int or long |
Int64 | A 64-bit signed integer | __int64 |
UInt16 | A 16-bit unsigned integer | unsigned short |
UInt32 | A 32-bit unsigned integer | unsigned int or unsigned long |
UInt64 | A 64-bit unsigned integer | unsigned __int64 |
Single | A single-precision, 32-bit, floating-point number | float |
Double | A double-precision, 64-bit, floating-point number | double |
Boolean | A Boolean value | bool |
Char | A 16-bit Unicode character | wchar_t |
Decimal | A 96-bit decimal value | Decimal |
IntPtr | A signed integer whose size depends on the platform | No built-in type |
UIntPtr | An unsigned integer whose size depends on the platform | No built-in type |
Note that the C++ equivalents are simply names for the types—aliases, if you like—that are rather more C++ like in nature. Although it’s more natural to use the native language equivalents, you could use the underlying .NET types instead, which means that the following two lines of code mean exactly the same thing, for example:
int n = 0; // use managed C++ type System::Int32 n = 0; // use .NET native type
A value type is a type that inherits from the System::ValueType class. Value types have several special properties:
Value types are stored on the stack (unlike references, which are stored on the run-time heap).
Instances of value types are always accessed directly (unlike reference types, which are accessed through references). Direct access means that you don’t use the new operator when creating instances.
Copying value types copies the value, rather than the reference.
As you can see, value types behave just like the standard built-in types, such as int and char, and they are just as efficient to use. As mentioned in the previous section, the main difference between value types and built-in types is that value types can also be treated as objects when necessary.
|