您的位置:首页 > 编程语言 > C语言/C++

C++/CLI学习入门数组

2012-11-13 21:50 302 查看
要学习数组,必须先了解跟踪句柄。

一、跟踪句柄

跟踪句柄类似于本地C++指针,但也有很大区别。跟踪句柄确实存储着某个对象的地址,但当CLR压缩堆过程中改变了该对象的地址,则垃圾回收器自动更新句柄所包含的地址。我们不能像本地指针那样用跟踪句柄来执行地址的算术运算,也不允许对跟踪句柄进行强制类型转换。

在CLR堆中创建的对象必须被跟踪句柄引用。所有属于引用类型的对象都存储在堆中,因此为引用这些对象所创建的变量都必须是跟踪句柄。例如,String类型是引用类型,因此引用String对象的变量必须是跟踪句柄。值类型默认分配在堆栈上,但也可以用gcnew操作符将其存储在堆上。此外必须注意,在堆上分配的变量——其中包括所有CLR引用类型——都不能在全局作用域内声明。

通过在类型名称后加”^”符号,用于声明一个该类型的句柄。下面的例子声明了一个String类型的跟踪句柄proverb。

String^proverb;

在声明时句柄时,系统自动为其分配一个空值,该句柄不引用任何对象。也可显示地将某个句柄置为空值:

proverb=nullptr;

注意不能用0来表示空值。如果用0来初始化某个句柄,则0将自动转换为被引用类型的对象,而句柄则指向该对象。可以在声明句柄时显示的将其初始化:

String^saying=L"IusedtothinkIwasindecisivebutnowI??¡ê¡èmnotsosure.";

该语句首先在堆上创建一个包含等号右边字符串的String对象,然后将该对象的地址存入saying中。注意字符串字面值的类型为constwchar_t*而非String。类String提供了这样的方法使得constwchar_t*类型的字符串可以用来创建String类型的对象。

下面这条语句创建了值类型的句柄:

int^value=99;

该语句在堆上创建一个Int32型的值类型变量,然后将该变量的地址存入句柄value中。由于value是一种指针,因此不能直接参与算术运算,可使用*运算符对地址求值(类似于本地C++指针那样):

intresult=2*(*value)+15;

由于*value表示value所指向地址存储的数值,因此result的值为2*99+15=213。注意,当value作为运算式左值时,不需要*即可对value指向的变量赋值。

int^result=0;
result=2*(*value)+15;

首先创建了一个指向数值0的句柄result。(该语句会触发一条编译器警告,提示不能利用0来将句柄初始化为空值。)

第2条语句=号右边为数值,而左边为句柄,编译器将自动将右值赋予句柄所指向的对象,即将其转换为如下语句

*result=2*(*value)+15;

注意,要采用上面的语句,result句柄必须实际定义过。如果仅仅声明了result,则会产生运行时错误

int^result;
*result=2*(*value)+15;


这是因为第二句要对地址result求值,即意味着result指向的对象已经存在,但实际并非如此,因为声明该对象时系统默认赋予其空值(nullptr)。在这种情况下,采用下面的方法就可以正常工作了

int^result;
result=2*(*value)+15;

二、数组

(一)数组句柄

CLR数组是分配在可回收垃圾堆上的。必须用array<typename>指出要创建的数组,同其它CLR堆上的对象一样,需要用句柄来访问它,例子如下:

array<int>^data;

数组句柄data可用于存储对元素类型为int的一维数组的引用。下面的例子声明了一个句柄,并新建一个CLR数组来对此句柄初始化。

array<int>^data=gcnewarray<int>(100);

和本地C++数组一样,CLR数组中元素的索引值也是从0开始的,可以通过[]访问数组元素。数组元素都是CLR对象,在上面的例子中数组元素为Int32型对象,它们在算术表达式中就像普通的整数类型一样。

Length属性是数组的一个重要属性,记录着数组元素的数量。保存了64位的数组长度。

for(inti=0;i<data->Length;i++)
data[i]=2*(i+1);

可以用foreach循环遍历数组元素。

array<int>^value={3,5,6,8,6};
foreach(intiteminvalue)
{
item=2*item+1;
Console::WriteLine("{0,5}",item);
}

该循环输出5字符宽度的字段,以右对齐的方式输出当前元素的计算结果,输出如下:

711131713

数组句柄可以被重新赋值,只要保持数组元素类型和维数(等级)不变即可,在前面例子中的数组句柄data指向一个int类型的一维数组,可以重新给它赋值,使其指向另外的int类型1维数组:

data=gcnewarray<int>(45);

数组可以在创建时通过元素列表初始化,下例在CLR堆上创建了一个double类型的数组,并将引用赋值给了数组句柄:

array<double>^sample={3.4,2.3,6.8,1.2,5.5,4.9,7.4,1.6};

如果在声明数组句柄时不进行初始化,那么在给句柄赋值时不能采用上面的方法直接用元素列表用作右值,而必须采用显示创建的方式。即不能

array<double>^sample;
sample={3.4,2.3,6.8,1.2,5.5,4.9,7.4,1.6}

而必须采用如下方式

array<double>^sample;
sample=gcnewarray<double>{3.4,2.3,6.8,1.2,5.5,4.9,7.4,1.6}

对于字符串数组,注意每一个元素也是引用类型,这是因为每一个元素String也是在CLR堆上创建的,因此也要用String^来访问它。

array<String^>^names={"Jack","John","Joe","Jessica","Jim","Joanna"};

可以用Array类静态函数Clear()对数组中的连续数组元素清零。

Array::Clear(samples,0,samples->Length);

Clear()函数的第一个参数是被清零的数组,第二个参数是要清除地第一个元素的索引,第三个参数为要清除地元素数量。因此上述语句将samples数组的所有元素都置为0。如果Clear()清除的是某个跟踪句柄,则句柄所对应的元素都被应用Clear()函数。如果元素为bool型,则被置为false。

(二)数组排序

Array类还定义了一个Sort()静态函数,可用于对数组进行排序。如果以数组句柄作为参数,则对整个数组排序。如果要对数组部分排序,则还需要增加元素起始索引及数量,如下例

array<int>^samples={27,3,54,11,18,2,16};
Array::Sort(samples,2,3);

排序后数组元素变为{27,3,11,18,54,2,16}

Sort函数还有很多其它版本,下面的例子展示了如何排序两个相关的数组,即第一个数组中的元素是第二个数组对应元素的键。对第一个数组排序后,可对第二个数组进行相应的调整,使得键与值相互对应。

Sort()函数对2个数组排序时,用第一个数组参数来确定两个数组的顺序,以此保持2个数组元素对应关系不变。

----------------<<==华丽的分割线::开始==>>[Ex4_13.cpp]-------------------------------------------
//Ex4_13.cpp:mainprojectfile.
#include"stdafx.h"
usingnamespaceSystem;

intmain(array<System::String^>^args)
{
array<String^>^names={"Jill","Ted","Mary","Eve","Bill","Al"};
array<int>^weights={103,168,128,115,180,176};

Array::Sort(names,weights);
foreach(String^nameinnames)
Console::Write(L"{0,10}",name);
Console::WriteLine();

foreach(intweightinweights)
Console::Write(L"{0,10}",weight);
Console::WriteLine();

return0;
}

----------------<<==华丽的分割线::结束==>>[Ex4_13.cpp]-------------------------------------------
输出为:
AlBillEveJillMaryTed
176180115103128168

(三)数组搜索

Array类还提供了函数BinarySearch()以使用对分法搜索算法,对一维数组或给定范围内搜索特定元素的索引位置。使用该函数要求数组必须是顺序排列的,因此在搜索之前必须对数组进行排序。

array<int>^value={23,45,68,94,123,150,203,299};
inttoBeFound=127;
intposition=Array::BinarySearch(value,toBeFound);
if(position<0)
Console::WriteLine(L"{0}wasnotfound.",toBeFound);
else
Console::WriteLine(L"{0}wasfoundatindexposition{1}",toBeFound,position);

Array::BinarySearch()的第一个参数是被搜索数组的句柄,第二个参数是要查找的内容,返回值为int类型的数值。如果返回值小于0则说明未找到。如果要指定搜索范围,则需要传递4个参数,其中第2参数为搜索起始索引,第3参数为搜索的元素数量,第4个是要搜索的内容。下面的代码从第4个元素开始,一直搜索到结束位置。

array<int>^value={23,45,68,94,123,150,203,299};
inttoBeFound=127;
intposition=Array::BinarySearch(value,3,6,toBeFound);

----------------<<==华丽的分割线::开始==>>[Ex4_14.cpp]-------------------------------------------
//Ex4_14.cpp:mainprojectfile.
#include"stdafx.h"
usingnamespaceSystem;

intmain(array<System::String^>^args)
{
array<String^>^names={"Jill","Ted","Mary","Eve","Bill","Al","Ned","Zoe","Dan","Jean"};
array<int>^weights={103,168,128,115,180,176,209,98,190,130};
array<String^>^toBeFound={"Bill","Eve","Al","Fred"};

intresult=0;
Array::Sort(names,weights);

foreach(String^nameintoBeFound)
{
result=Array::BinarySearch(names,name);
if(result<0)
Console::WriteLine(L"{0}wasnotfound.",name);
else
Console::WriteLine(L"{0}weights{1}lbs.",name,weights[result]);
}

return0;
}

----------------<<==华丽的分割线::开始==>>[Ex4_14.cpp]-------------------------------------------
当搜索不到目标时,Array::BinarySearch()函数输出的并非任意负数,而是第一个大于该目标的元素索引值的按位补码。利用该方法,可以不打乱顺序在数组中插入新值。如,我们希望插入”Fred”到names数组中

array<String^>^names={"Jill","Ted","Mary","Eve","Bill","Al","Ned","Zoe","Dan","Jean"}
Array::Sort(names);
String^name=L"Fred";
intposition=Array::BinarySearch(names,name);
if(position<0)
position=~position;

此时,position保存的是大于Fred的第一个元素的位置,该数值可用于插入新值。

array<String^>^newNames=gcnewarray<String^>(names->Length+1);
for(inti=0;i<position;i++)
newNames[i]=names[i];
newNames[position]=name;

if(position<name->Length)
for(inti=position;i<names->Length;i++)
newNames[i+1]=names[i];
names=nullptr;

注意:最后一句用于删除names数组。

(四)多维数组

C++/CLI中可以创建多维数组,最大维数32维。与ISO/ANSIC++不同的是,C++/CLI中的多维数组并非数组的数组,而是真正的多维数组,创建整数多维数组方法如下:

array<int2>^value=gcnewarray<int,2>(4,5);

上面的代码创建了一个二维数组,四行五列,共20个元素。访问的方法是利用多个用逗号分隔的索引值来访问每一个元素,而不能用一个索引值访问一行

intnrows=4;
intncols=5;
array<int,2>^value=gcnewarray<int,2>(nrows,ncols);
for(inti=0;i<nrows;i++)
for(intj=0;j<ncols;j++)
value[i,j]=(i+1)*(j+1);

上面的代码利用循环给二维数组value赋值。这里访问二维数组元素的符号与本地C++不同:后者实际上是数组的数组,而C++/CLI是真正的二维数组,不能用一个索引值来访问二维数组,那样是没有意义的。数组的维数被称为等级,上面value数组的等级为2。而本地C++数组的等级始终为1。当然,在C++/CLI中也可以定义数组的数组,方法见下例

----------------<<==华丽的分割线::开始==>>[Ex4_15.cpp]-------------------------------------------
//Ex4_15.cpp:mainprojectfile.
#include"stdafx.h"
usingnamespaceSystem;

intmain(array<System::String^>^args)
{
constintSIZE=12;
array<int,2>^products=gcnewarray<int,2>(SIZE,SIZE);

for(inti=0;i<SIZE;i++)
for(intj=0;j<SIZE;j++)
products[i,j]=(i+1)*(j+1);
Console::WriteLine(L"Hereisthe{0}timestable:",SIZE);

//Writehorizontaldividerline
for(inti=0;i<=SIZE;i++)
Console::Write(L"_____");
Console::WriteLine();

//Writetoplineoftable
Console::Write(L"|");
for(inti=1;i<=SIZE;i++)
Console::Write("{0,3}|",i);
Console::WriteLine();

//Writehorizontaldividerlinewithverticals
for(inti=0;i<=SIZE;i++)
Console::Write("____|",i);
Console::WriteLine();

//Writeremaininglines
for(inti=0;i<SIZE;i++)
{
Console::Write(L"{0,3}|",i+1);
for(intj=0;j<SIZE;j++)
Console::Write("{0,3}|",products[i,j]);
Console::WriteLine();
}

//Writehorizontaldividerline
for(inti=0;i<=SIZE;i++)
Console::Write("_____",i);
Console::WriteLine();
return0;
}

----------------<<==华丽的分割线::结束==>>[Ex4_15.cpp]-------------------------------------------
上面的例子创建了一个12x12的乘法表,输出如下:

Hereisthe12timestable:
_________________________________________________________________
|1|2|3|4|5|6|7|8|9|10|11|12|
____|____|____|____|____|____|____|____|____|____|____|____|____|
1|1|2|3|4|5|6|7|8|9|10|11|12|
2|2|4|6|8|10|12|14|16|18|20|22|24|
3|3|6|9|12|15|18|21|24|27|30|33|36|
4|4|8|12|16|20|24|28|32|36|40|44|48|
5|5|10|15|20|25|30|35|40|45|50|55|60|
6|6|12|18|24|30|36|42|48|54|60|66|72|
7|7|14|21|28|35|42|49|56|63|70|77|84|
8|8|16|24|32|40|48|56|64|72|80|88|96|
9|9|18|27|36|45|54|63|72|81|90|99|108|
10|10|20|30|40|50|60|70|80|90|100|110|120|
11|11|22|33|44|55|66|77|88|99|110|121|132|
12|12|24|36|48|60|72|84|96|108|120|132|144|
_________________________________________________________________

其中创建二维数组的代码如下:

constintSIZE=12;
array<int,2>^products=gcnewarray<int,2>(SIZE,SIZE);

第一行定义了一个整型常量SIZE,用于指定每一维数组的元素数量,第二行代码定义了一个等级2的数组,为12x12大小,该数组用于存储12x12的乘法表乘积。然后在嵌套循环中给数组赋值,大部分代码用于格式化输出以使其更加美观,这里就不再说明。

(五)数组的数组

如果数组的元素是引用数组的跟踪句柄,那么就可以创建数组的数组。同时,每一维数组的长度可以不同,即所谓的“锯齿形数组”。例如,用ABCDE来表示学生的成绩等级,根据等级分组存储班内学生的姓名,则可以创建一个包含5个元素的数组,每个元素为一个姓名数组(即字符串数组)

array<array<String^>^>^grades=gcnewarray<array<String^>^>(5)

利用上面创建的数组,然后可以创建5个姓名数组了

grades[0]=gcnewarray<String^>{"Louise","Jack"};
grades[1]=gcnewarray<String^>{"Bill","Mary","Ben","Joan"};
grades[2]=gcnewarray<String^>{"Jill","Will","Phil"};
grades[3]=gcnewarray<String^>{"Ned","Fred","Ted","Jed","Ed"};
grades[4]=gcnewarray<String^>{"Dan","Ann"};

grades
访问grades数组的第n个元素,而各元素为指向String^类型数组的句柄,因此上面的语句用于创建了String对象句柄的数组,并将创建数组的地址赋值给了grades数组元素。同时,这些字符串数组的长度是不同的。

上面的语句也可以用一个初始化语句来实现

array<array<String^>^>^grades=gcnewarray<array<String^>^>
{
gcnewarray<String^>{"Louise","Jack"},
gcnewarray<String^>{"Bill","Maray","Ben","Joan"},
gcnewarray<String^>{"Jill","Will","Phil"},
gcnewarray<String^>{"Ned","Fred","Ted","Jed","Ed"},
gcnewarray<String^>{"Dan","Ann"},
};

注意:元素的初值必须写在花括号里。

----------------<<==华丽的分割线::开始==>>[Ex4_16.cpp]-------------------------------------------
//Ex4_16.cpp:mainprojectfile.
#include"stdafx.h"
usingnamespaceSystem;

intmain(array<System::String^>^args)
{
array<array<String^>^>^grades=gcnewarray<array<String^>^>
{
gcnewarray<String^>{"Louise","Jack"},
gcnewarray<String^>{"Bill","Maray","Ben","Joan"},
gcnewarray<String^>{"Jill","Will","Phil"},
gcnewarray<String^>{"Ned","Fred","Ted","Jed","Ed"},
gcnewarray<String^>{"Dan","Ann"}
};

wchar_tgradeLetter='A';
foreach(array<String^>^gradeingrades)
{
Console::WriteLine(L"StudentswithGrade{0}:",gradeLetter++);
foreach(String^studentingrade)
Console::Write("{0,12}",student);
Console::WriteLine();
}
return0;
}

----------------<<==华丽的分割线::结束==>>[Ex4_16.cpp]-------------------------------------------
输出为

StudentswithGradeA:
LouiseJack
StudentswithGradeB:
BillMarayBenJoan
StudentswithGradeC:
JillWillPhil
StudentswithGradeD:
NedFredTedJedEd
StudentswithGradeE:
DanAnn
摘:http://www.cppblog.com/golq/archive/2009/06/29/88733.html
更多:http://www.cppblog.com/golq/category/11113.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: