Click here to Skip to main content
Email Password   helpLost your password?

Introduction

Managed arrays are allocated on the CLI heap as opposed to native arrays which are allocated on the unmanaged C++ heap, which essentially means that they are typical garbage collected .NET objects. And throughout this article the term array will refer to a managed array, and if at all a native array is mentioned, it will explicitly be termed as a native array. Some of you might have seen my article on using managed arrays with the old MC++ syntax and I am sure a few of you would have let out pained groans at the really peculiar syntax. Well, those people should be delighted to know that C++/CLI supports managed arrays using a far more intuitive syntax than which existed in the older syntax.

Basic features

Here are some of the basic features of managed arrays in C++/CLI :-

Pseudo-template of an array type

The declaration and usage of array types in C++/CLI seems to use an imaginary template type (there is no such template of course, it�s all VC++ compiler magic) :-

namespace stdcli::language
{
    template<typename T, int rank = 1>
        ref class array : System::Array {};
}

array is declared inside the stdcli::language namespace so as to avoid conflicts with existing source code

Single dimensional array usage

Arrays of a reference type

ref class R
{
public:
    void Test1(int x)
    {
        array<String^>^ strarray = gcnew array<String^>(x);
        for(int i=0; i<x; i++)
            strarray[i] = String::Concat("Number ",i.ToString());
        for(int i=0; i<x; i++)
            Console::WriteLine(strarray[i]);
    }
};

The syntax does look different from that used for native arrays; C++ coders who have used templates should find this a lot more intuitive than people who come from a non-C++ background, but eventually they'll get comfortable with it.

Arrays of a value type

ref class R
{
public:
    void Test2(int x)
    {
        array<int>^ strarray = gcnew array<int>(x);
        for(int i=0; i<x; i++)
            strarray[i] = i * 10;
        for(int i=0; i<x; i++)
            Console::WriteLine(strarray[i]);
    }
};

Unlike in the old syntax, array syntax for value types is exactly the same as that for managed types.

Multi dimensional array usage

Multi dimensional arrays are managed arrays that have a rank greater than 1. They are not arrays of arrays (those are jagged arrays).

ref class R
{
public:
    void Test3()
    {
        array<String^,2>^ names = gcnew array<String^,2>(4,2);
        names[0,0] = "John";
        names[1,0] = "Tim";
        names[2,0] = "Nancy";
        names[3,0] = "Anitha";
        for(int i=0; i<4; i++)
            if(i%2==0)
                names[i,1] = "Brown";
            else
                names[i,1] = "Wilson";
        for(int i=0; i<4; i++)
            Console::WriteLine("{0} {1}",names[i,0],names[i,1]);
    }    
};

Jagged arrays

Jagged arrays are non-rectangular, and are actually arrays of arrays. The new template-style array syntax simplifies the declaration and use of jagged arrays, which is a major improvement over the old syntax where jagged arrays had to be artificially simulated by the developer.

ref class R
{
public:
  void Test()
  {
    array<array<int>^>^ arr = gcnew array<array<int>^> (5); 

    for(int i=0, j=10; i<5; i++, j+=10)
    {
      arr[i] = gcnew array<int> (j);
    }

    Console::WriteLine("Rank = {0}; Length = {1}",
      arr->Rank,arr->Length);
    /*
      Output :-
        Rank = 1; Length = 5
    */

    for(int i=0; i<5; i++)
      Console::WriteLine("Rank = {0}; Length = {1}",
        arr[i]->Rank,arr[i]->Length);
    /*
      Output :-
        Rank = 1; Length = 10
        Rank = 1; Length = 20
        Rank = 1; Length = 30
        Rank = 1; Length = 40
        Rank = 1; Length = 50
    */
  }
};

Using a typedef to simplify jagged array usage

typedef array<array<int>^> JaggedInt32Array;
typedef array<array<String^>^> JaggedStringArray;

ref class R
{
public:
    void Test()
    {
        JaggedInt32Array^ intarr = gcnew JaggedInt32Array(10);
        JaggedStringArray^ strarr = gcnew JaggedStringArray(15);        
    }
};

Directly initialize an array

The new syntax allows painless direct initialization of arrays.

ref class R1
{
public:
  void Test()
  {
    //Single dimensional arrays

    array<String^>^ arr1 = gcnew array<String^> {"Nish", "Colin"};
    array<String^>^ arr2 = {"Nish", "Smitha"};
    
    //Multi dimensional arrays

    array<Object^,2> ^ multiobarr = {{"Nish", 100}, {"Jambo", 200}};
  }
};

Arrays as function arguments

ref class R
{
public:
  void ShowStringArray(array<String^>^ strarr)
  {
    for(int i=0; i<strarr->Length; i++)
      Console::WriteLine(strarr[i]);
  }
  void Show2DInt32Array(array<int,2>^ arr)
  {    
    for(int i=0; i<arr->GetLength(0); i++)
    {
      Console::WriteLine("{0} times 2 is {1}",arr[i,0],arr[i,1]);
    }
  }
};

void _tmain()
{
  R^ r = gcnew R();
  r->ShowStringArray(gcnew array<String^> {"hello", "world"});
  array<int,2>^ arr = { {1,2}, {2,4}, {3,6}, {4,8} };
  r->Show2DInt32Array(arr);
}

//Output :-


/*
  hello
  world
  1 times 2 is 2
  2 times 2 is 4
  3 times 2 is 6
  4 times 2 is 8
*/

Returning arrays from functions

ref class R
{
public:
    array<String^>^ GetNames(int count)
    {
        array<String^>^ arr = gcnew array<String^>(count);
        for(int i=0; i<count; i++)
        {
            arr[i] = String::Concat("Mr. ",(i+1).ToString());
        }
        return arr;
    }

    void ShowNames(array<String^>^ arr)
    {
        for(int i=0; i<arr->Length; i++)
            Console::WriteLine(arr[i]);
    }
};

void _tmain()
{
    R^ r = gcnew R();
    array<String^>^ arr = r->GetNames(7);
    r->ShowNames(arr);
}

Array covariance

You can assign an array of type R to an array of type B, where B is a direct or indirect parent of R, and R is a ref class.

ref class R1
{
    //...

};

ref class R2 : R1
{
    //...

};

void _tmain()
{
    array<R1^>^ arr1 = gcnew array<R1^>(4);
    array<R2^>^ arr2 = gcnew array<R2^>(4);    
    
    array<R1^>^ arr3 = arr2;    
    array<R1^>^ arr4 = gcnew array<R2^>(4);    
}

Parameter arrays

C++/CLI has support for parameter arrays. There can only be one such parameter array per function and it also needs to be the last parameter.

ref class R
{
public:
    void Test(String^ s, [ParamArray] array<int>^ arr )    
    {
        Console::Write(s);
        for(int i=0; i<arr->Length; i++)
            Console::Write(" {0}",arr[i]);
        Console::WriteLine();
    }
};

void _tmain()
{
    R^ r = gcnew R();
    r->Test("Nish");
    r->Test("Nish",1);
    r->Test("Nish",1,15);
    r->Test("Nish",1,25,100);
}

Right now the only supported syntax uses the ParamArray attribute, but the eventual syntax will also support the simpler style shown below :-

Calling System::Array methods

Since every C++/CLI array is implicitly a System::Array object, we can use System::Array methods on CLI arrays.

ref class R
{
public:
  bool CheckName(array<String^>^ strarr, String^ str)
  {
    Array::Sort(strarr);
    return Array::BinarySearch(strarr,str) < 0 ? false : true;
  }
};

void _tmain()
{
  R^ r = gcnew R();
  array<String^>^ strarr = {"Nish","Smitha",
    "Colin","Jambo","Kannan","David","Roger"};  
  Console::WriteLine("Nish is {0}",r->CheckName(strarr,"Nish") ? 
    gcnew String("Present") : gcnew String("Absent"));
  Console::WriteLine("John is {0}",r->CheckName(strarr,"John") ? 
    gcnew String("Present") : gcnew String("Absent"));
}

I've used System::Sort and System::BinarySearch in the above example.

Here's another snippet that clearly demonstrates the implicit conversion to System::Array.

ref class R
{
public:
    void ShowRank(Array^ a)
    {
        Console::WriteLine("Rank is {0}",a->Rank);
    }
};

void _tmain()
{
    R^ r = gcnew R();    
    r->ShowRank( gcnew array<int>(10) );
    r->ShowRank( gcnew array<String^,2>(10,2) );
    r->ShowRank( gcnew array<double,3>(10,3,2) );    
    r->ShowRank( gcnew array<int> {1,2,3} );
    r->ShowRank( gcnew array<int,2> {{1,2}, {2,3}, {3,4}} );
}

Arrays of non-CLI objects

You can declare C++/CLI arrays where the array type is of a non-CLI object. The only inhibition is that the type needs to be a pointer type. Consider the following native C++ class :-

#define Show(x) Console::WriteLine(x)

class N
{
public:
   N()
   {
      Show("N::ctor");
   }
   ~N()
   {
      Show("N::dtor");
   }
};

Now here's how you can declare an array of this type :-

ref class R
{
public:   
   void Test()
   {
      array<N*>^ arr = gcnew array<N*>(3);
      for(int i=0; i<arr->Length; i++)   
         arr[i] = new N();
   }
};

Put this class to use with the following test code :-

void _tmain()
{
   R^ r = gcnew R();
   r->Test();
   Show("Press any key...");
   Console::ReadKey();
}

/* Output:

N::ctor
N::ctor
N::ctor
Press any key...

*/

There, that worked. Of course the destructors for the array elements did not get called, and in fact they won't get called even if a Garbage Collection takes place and the array object is cleaned up. Since they are native objects on the standard C++ heap, they need to have delete called on them individually.

ref class R
{
public:   
   void Test()
   {
      array<N*>^ arr = gcnew array<N*>(3);
      for(int i=0; i<arr->Length; i++)   
         arr[i] = new N();
      for(int i=0; i<arr->Length; i++)   
         delete arr[i];
   }
   //...


/* Output

N::ctor
N::ctor
N::ctor
N::dtor
N::dtor
N::dtor
Press any key...

*/

Ok, that's much better now. Or if you want to avoid calling delete on each object, you can alternatively do something like this :-

ref class R
{
public:   
   void Test()
   {
      N narr[3];
      array<N*>^ arr = gcnew array<N*>(3);
      for(int i=0; i<arr->Length; i++)
         arr[i] = &narr[i];   
   }   
};

This yields similar results to the above snippet. Alternatively you could init the array members in its containing class's constructor and delete them in the destructor, and then use the containing class as an automatic variable (C++/CLI supports deterministic destruction).

Direct manipulation of CLI arrays using native pointers

Here's some code that shows how you can directly manipulate the contents of an array using native pointers. The first sample is for a single dimensional array and the second is for a jagged array.

Natively accessing a single-dimensional array

void Test1()    
{
    array<int>^ arr = gcnew array<int>(3);
    arr[0] = 100;
    arr[1] = 200;
    arr[2] = 300;

    Console::WriteLine(arr[0]);
    Console::WriteLine(arr[1]);
    Console::WriteLine(arr[2]);

    /*
    Output :-
      100
      200
      300
    */

    // Modifying the array using a native int* 

    // that points to a pinned pointer in GC'd heap

    pin_ptr<int> p1 = &arr[0];
    int* p2 = p1;
    while(*p2)
    {           
        (*p2)++;
        p2++;
    }

    Console::WriteLine(arr[0]);
    Console::WriteLine(arr[1]);
    Console::WriteLine(arr[2]);

    /*
    Output :-
      101
      201
      301
    */
} 

Natively accessing a jagged array

void Test2()
{
    array<array<int>^>^ arr = gcnew array<array<int>^>(2);
    arr[0] = gcnew array<int>(2);
    arr[1] = gcnew array<int>(2);
    arr[0][0] = 10;
    arr[0][1] = 100;
    arr[1][0] = 20;
    arr[1][1] = 200;

    Console::WriteLine(arr[0][0]);
    Console::WriteLine(arr[0][1]);
    Console::WriteLine(arr[1][0]);
    Console::WriteLine(arr[1][1]);

    /*
    Output:
      10
      100
      20
      200
    */


    // Copying the managed jagged array to

    // a native array of pointers and accessing

    // the members using the native array

    pin_ptr<int> p1 = &arr[0][0];
    pin_ptr<int> p2 = &arr[1][0];
    int* p3[2];
    p3[0] = p1;
    p3[1] = p2;

    Console::WriteLine(p3[0][0]);
    Console::WriteLine(p3[0][1]);
    Console::WriteLine(p3[1][0]);
    Console::WriteLine(p3[1][1]);

    /*
    Output:
      10
      100
      20
      200
    */

    // Assigning the native array of pointers

    // to a native int** and modifying the array

    int** p4 = p3;
    for(int i=0; i<2; i++)
        for(int j=0; j<2; j++)
            p4[i][j] += 3;          

    Console::WriteLine(arr[0][0]);
    Console::WriteLine(arr[0][1]);
    Console::WriteLine(arr[1][0]);
    Console::WriteLine(arr[1][1]);

    /*
    Output:
      13
      103
      23
      203
    */
}

Essentially we use a pinning pointer to the GC'd heap and then treat the casted native pointer as if it were pointing to a native array. Gives us a really fast method to manipulate array content!

Conclusion

Array syntax in C++/CLI is definitely an aesthetic improvement over the older MC++ syntax, and it also brings in a consistency of syntax that was severely lacking earlier. The template-style syntax should give a natural feel for C++ coders though it might take a little while before you fully get used to it. As usual, I request you to freely post any comments, suggestions, criticism, praise etc. that you might have for me.

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generaltry
sahrulijam
8:44 17 Aug '09  
simple tutorial at http://www.wiqqet.com Laugh

hello

QuestionWhat do you mean when you say implicit conversion?
knockNrod
4:23 11 Sep '08  
I'm trying to return a multi-dimensional (native) array of data from C++/CLI to a Visual Basic.Net application. I define the function's return value as a managed array, but return a reference to the native array, and it tells me it cannot convert from int[5] to System::Array ^. (I tried simplifying to a single-dimensional array, but the data is actually three-dimensional.)

When I went back and read your article more carefully, it doesn't appear to be deomonstrating implicit conversion from a native array to a System::Array type, so I'm wondering exactly what your definition is of implicit conversion from a native array to a managed array.

Without darkness, there are no dreams.
-Karla Kuban

GeneralFunny syntax for multi dimensions
mef526
8:34 19 Jun '06  
I've never seen this:
a[1,2,3]
Isn't this more correct:
a[1][2][3]

GeneralRe: Funny syntax for multi dimensions
Jun Du
9:16 19 Jun '06  
Not really. [1, 2, 3] is closer to mathematical convention. Both coordinate (1-3 dimensions) and vector (n-dimensions) use this syntax (of course with () not []).



- It's easier to make than to correct a mistake.
GeneralRe: Funny syntax for multi dimensions
mef526
11:48 19 Jun '06  
So you're saying that C/C++ uses this syntax?
a[1,2,3]
I've never seen it...

GeneralRe: Funny syntax for multi dimensions
Nishant Sivakumar
2:49 20 Jun '06  
mef526 wrote:
So you're saying that C/C++ uses this syntax?
a[1,2,3]
I've never seen it...

This article is on using CLI arrays in C++/CLI - CLI arrays are not the same as standard C++ arrays, and thus the syntax is different.

If you haven't seen the syntax before, it's probably because you've never previously used C++/CLI. The standard syntax is still used in C++/CLI, when you work with native arrays.

Regards,
Nish
Nish’s thoughts on MFC, C++/CLI and .NET (my blog)
Currently working on C++/CLI in Action for Manning Publications.
Also visit the Ultimate Toolbox blog (New)

GeneralRe: Funny syntax for multi dimensions
mef526
4:43 20 Jun '06  
Thanks for straightening that out for me. I haven't used VS2005 much since it doesn't allow mixed mode debugging & editing. You either debug or edit but not both. Most of my development now has a VB interface on a C++ back end DLL. Back in 2002 I chose to abandon VS6 for .NET and I really regret it now. Looks like MS is out to redefine C++ now. For programmers that have legacy code to support, like most automated test systems, they have forgotten about us.
GeneralRe: Funny syntax for multi dimensions
Jun Du
4:55 20 Jun '06  
C++/CLI is the CLI extension of ISO/IEC Standard C++. Like its previous C++ compiler, MS always supports a limited number of extensions. Only this time it takes a serious take on .NET:
1) C++/CLI becomes one of the .NET compliant languages. In short, if you want to take advantage of .NET Framework features, like GC, you have to use CLI syntax.
2) MS subcribed C++/CLI spec as Standard ECMA-335 and then ECMA-372. BTW, C++/CLI was developed based on previous "Managed Extensions for C++" (with syntaxes containing weird underscores).

- It's easier to make than to correct a mistake.
GeneralHow to Create Label Array on the Run-Time
dataminers
4:58 14 Jun '06  
How to Create Label Array on the Run-Time ?

I TRY Label[] label1 = new Label[labelCount]; NOT WORK
I TRY array<Label^>^ lblArray = gcnew array<Label^>(10); ITS WORK, BUT I DETERMINE POINT...etc

Best REGARDS
GeneralHelp me!
sangtao
0:57 21 Jan '06  
I have a class:

class FileArray{
streampos filePtr; // streampos = a position in fstream
fstream fs;
public:
FileArray ( char *fName ):fs( fName, ios :: in | ios :: out ){
// open 4 IO
}
FileArray& operator[] (long x); // overload [ ]
FileArray& operator= (char c); // overload =
operator char (); // overload char
};
FileArray& FileArray :: operator[] (long x) {
filePtr = streampos(x);
return *this; }

FileArray& FileArray :: operator= (char buf) {
fs.seekp( filePtr, ios :: beg );
fs.write ( &buf, 1 );
fs.flush();
return *this;
}

FileArray :: operator char() {
char buf;
fs.seekg( filePtr, ios :: beg );
fs.read( &buf, 1 );
return buf;
}

int main()
{
FileArray source("source.txt"), dest("dest.txt");
char c = 'a';
for (int j=0;j<5;j++){
source[j]=c;
}

for (int j=0;j<5;j++){
dest[j] = source[j];
}
return 0;
}

why is dest.txt empty? please give a solution. thanks for your help!

hoale
Generaldynamically allocated arrays
PRMARJORAM
5:19 1 Dec '05  
How do i dynamically add items to an array?
Is a little restrictive that they must always be fixed size!



PR Marjoram BSc
GeneralArrays/Pinned Pointers
jcarlaft
5:39 17 Jan '05  
Is it advisable to use Pinned Pointers on managed arrays if I'm passing the arrays to unmanaged code? I am calling functions from unmanaged code and passing data in/out as arrays. I had read your article on Pinned Pointers and it mentioned using these for Interop code.
GeneralRe: Arrays/Pinned Pointers
bschwagg
11:40 5 Jun '06  
I'm doing the same thing. Also, my native (unmanaged) arrays are static size 64. The compiler only allows up to 32. What the heck?
GeneralIs MFC and ATL going out......
Aji Varghese
3:28 14 Oct '04  
Is MFC and ATL going.........

is native programming not allowed in the comming days.......
GeneralRe: Is MFC and ATL going out......
Nishant S
18:18 14 Oct '04  
Aji Varghese wrote: Is MFC and ATL going.........

is native programming not allowed in the comming days.......

You can continue to use MFC and ATL, and in fact you can mix MFC ad ATL with CLI stuff if you wanted to do so. That's one of the reasons that makes C++/CLI the number-one language targetting the .NET framework.


My blog on C++/CLI, MFC/Win32, .NET - void Nish(char* szBlog); My MVP tips, tricks and essays web site - www.voidnish.com
GeneralCOMPLICATED....
Aji Varghese
3:24 14 Oct '04  

why this looks complicated..........


GeneralRe: COMPLICATED....
Nishant S
18:16 14 Oct '04  
Aji Varghese wrote: why this looks complicated..........
It's simpler than the older MC++ syntax and more intuitive than the C# syntax.


My blog on C++/CLI, MFC/Win32, .NET - void Nish(char* szBlog); My MVP tips, tricks and essays web site - www.voidnish.com
General'array': undeclared identifier
Jefus
21:01 14 Sep '04  
I'm using Visual C++ 2005 Express Beta1, and I get error C2065: 'array' : undeclared identifier when compiling

      void Test1(int x)
      {
            array<String^>^ strarray = gcnew array<String^>(x);
            for(int i=0; i<x; i++)
                  strarray[i] = String::Concat("Number ",i.ToString());
            for(int i=0; i<x; i++)
                  Console::WriteLine(strarray[i]);
      }

GeneralRe: 'array': undeclared identifier
Nishant S
21:10 14 Sep '04  
I hope you are compiling with /clr (the default might be the old mc++ syntax)

Nish


My blog on C++/CLI, MFC/Win32, .NET - void Nish(char* szBlog); My MVP tips, tricks and essays web site - www.voidnish.com
GeneralRe: 'array': undeclared identifier
marco_it
0:00 1 Oct '04  
Did you forget the namespace declaration?

//compile with: /clr
using namespace stdcli::language;
using namespace System;

ref class R
{
public:
void Test1(int x)
{
array^ strarray = gcnew array(x);
for(int i=0; i strarray[i] = String::Concat("Number ",i.ToString());
for(int i=0; i Console::WriteLine(strarray[i]);
}
};

Marco.

GeneralRe: 'array': undeclared identifier
parsley_w
0:03 9 Jun '06  
hi Dear Carco,

I am facing the same problem. I constantly get this error message : 'array': undeclared identifier. and I tried to include namespace stdcli::language. But the compiler doesn't recognize this namespace. I created C++ program under VS.net 2003.
any hints? Thanks so much!

Wang,Dayong

...
GeneralWhat's the best way to .NET
Majid Shahabfar
9:52 6 Aug '04  
Hi Nishhant,
I really don't know what's the best way to bulding .NET application.
I was already using MFC to create Windows application.
Know I wanna know which one is better to create .NET app C# or C++/CLI?
tnks
GeneralRe: What's the best way to .NET
Nishant S
10:06 6 Aug '04  
Majid Shahabfar wrote: I was already using MFC to create Windows application.
Get a copy of the VC++ Whidbey beta. You can continue using MFC and at the same time add .NET support to your apps.

Mix MFC, ATL, .NET all in the same app with C++/CLI Smile

Nish


My take on gmail - Is gmail just a fashion statement? My blog on C++/CLI, MFC/Win32, .NET - void Nish(char* szBlog); My MVP tips, tricks and essays web site - www.voidnish.com
GeneralWhich version do you use for your articles?
Nemanja Trifunovic
14:36 18 Jul '04  
I have VC++ 2005 Express Beta 1, and everything that I am interested in is missing: deterministic finalization, STL.NET, ref objects on managed heap and vice versa, generics are unusable...

Do you as a MVP have something that works better?


My programming blahblahblah blog. If you ever find anything useful here, please let me know to remove it.
GeneralRe: Which version do you use for your articles?
Nishant S
16:58 19 Jul '04  
Hi Nemanja

I am using an alpha version of the compiler (which actually has a few more features than the beta version that comes with Express versions).

This is what I could make out from the discussions going on in the beta forums :-

Right now the latest beta is a relatively stable version of a slightly
older version.

And the latest alpha is a relatively unstable version of a slightly newer
version.

The version I am using does support deterministic finalization/destruction, but as for the other features you mentioned, they don't work for me as well Frown

Nish


My take on gmail - Is gmail just a fashion statement? My blog on C++/CLI, MFC/Win32, .NET - void Nish(char* szBlog); My MVP tips, tricks and essays web site - www.voidnish.com


Last Updated 12 Aug 2004 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010