Click here to Skip to main content
Click here to Skip to main content
Go to top

Using value types in managed C++

, 15 Oct 2001
Rate this:
Please Sign up or sign in to vote.
A quick introduction to using .NET value types in managed C++
<!-- Article Starts -->

Introduction

Managed reference types in .NET are objects that contains methods and properties and which live their lives on the .NET managed heap. There are situations, however, where you do not want the overhead associated with creating and destroying objects on the heap. When dealing with simple data structures such as an integer or an (x,y) coordinate it is far more convenient - and efficient - to allocate these values directly on the stack and work with them using the standard value type semantics. No one wants to have to write
int* n = new int();

when declaring an integer (or array of 10,000 integers!). Instead, primitive types such as numeric types, boolean, char and date, structures and enumerations are all declared as value types, meaning they are allocated on the stack and declared and accessed as you would a structure or stack-based variable in standard C++.

Declaring and Creating Value Types

To declare a value type in managed C++ you use the new __value keyword. For instance, if we wished to create a data type to represent complex numbers we could declare the following:

__value struct Complex
{
    double real;
    double imaginary;
};

Value types are created on the stack and are accessed directly. Value types are declared

Complex z;

and their members accessed using the . syntax

z.real = 1.0;
z.imaginary = -3.1415;

Once the memory containing a value type is freed, the value type instance is destroyed. Hence, references to value types are not allowed. If it were allowed it would be possible to have a reference point to an invalid memory location. A value type will always point to a variable of that type, and cannot be null. Assigning a value type to another variable results in a copy of the value being made.

Because value types are managed types, they are initialised to 0 when they are created. Value types can contain reference types, though these reference types will be created on the managed heap and the reference to them stored on the stack.

Value types implicitly inherit from System.ValueType, and their use is encouraged for types that act like primitive data types, types that are small, types that do not inherit from, or are inherited by, other data types, and types that are not frequently passes as parameters (this would cause a lot of memory allocation and copying). Value types can inherit from managed interfaces, and can override virtual methods defined therein.

One important point with all value types is that they do can contain methods and fields, and you can override virtual methods from base interfaces. For instance, we may wish to override the ToString method for our complex type so that we can present it in a formatted manner

__value struct Complex
{
    double real;
    double imaginary;

    virtual String *ToString()
    {
        return String::Format("{0} + {1}i", real.ToString("N2"), imaginary.ToString("N2"));
    }
};

Boxing and Unboxing

Boxing is a feature that allows a value type to be passed around as a reference type. For instance, one of the the standard Format method overrides of the .NET String class takes a format string and an object.

public: static String* Format(String*, Object*);

The format string specifies the format, and the object is accessed within that format string using the {0} syntax. An int is not derived from Object and hence cannot normally be passed to this function. However, by boxing an integer value we are creating an object that will contain the value of the integer and can be used as an object. The syntax is

object obj = __box(5);

This creates a managed reference on the managed heap containing the value '5'. This object can now be used wherever an Object* object is required.

We can also declare boxed value types like

__box Complex* pZ = __box(z);

This gives us an object we can use in whatever way we deem fit, but in doing so we retain the ability to access the underlying type's data fields

pZ->real = 4;

Once we are done using the value in its boxed form we can unbox the value using the C++ dynamic_cast operator

Complex w = * dynamic_cast<Complex*>(pZ);

History

26 Apr 2001 - added sample app

16 Oct 2001 - updated source for VS.NET beta 2

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Chris Maunder
Founder CodeProject
Canada Canada
Chris is the Co-founder, Administrator, Architect, Chief Editor and Shameless Hack who wrote and runs The Code Project. He's been programming since 1988 while pretending to be, in various guises, an astrophysicist, mathematician, physicist, hydrologist, geomorphologist, defence intelligence researcher and then, when all that got a bit rough on the nerves, a web developer. He is a Microsoft Visual C++ MVP both globally and for Canada locally.
 
His programming experience includes C/C++, C#, SQL, MFC, ASP, ASP.NET, and far, far too much FORTRAN. He has worked on PocketPCs, AIX mainframes, Sun workstations, and a CRAY YMP C90 behemoth but finds notebooks take up less desk space.
 
He dodges, he weaves, and he never gets enough sleep. He is kind to small animals.
 
Chris was born and bred in Australia but splits his time between Toronto and Melbourne, depending on the weather. For relaxation he is into road cycling, snowboarding, rock climbing, and storm chasing.
Follow on   Twitter   Google+   LinkedIn

You may also be interested in...

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web04 | 2.8.140926.1 | Last Updated 16 Oct 2001
Article Copyright 2001 by Chris Maunder
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid