要学习数组,必须先了解跟踪句柄。

一、跟踪句柄

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

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

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

String^ proverb;

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

proverb = nullptr;

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

String^ saying = L"I used to think I was indecisive but now I??¡ê¡èm not so sure."; 

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

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

int^ value = 99; 

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

int result = 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 = gcnew array<int>(100); 

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

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

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

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

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

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

    7   11   13   17   13 

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

data = gcnew array<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 = gcnew array<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 : main project file.
#include "stdafx.h"
using namespace System;int main(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);for each( String^ name in names )Console::Write(L"{0, 10}", name);Console::WriteLine();for each(int weight in weights)Console::Write(L"{0, 10}", weight);Console::WriteLine();return 0;
}
- - - - - - - - - - - - - - - - <<== 华丽的分割线 ::结束==>> [Ex4_13.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
输出为: 
Al Bill Eve Jill Mary Ted
176 180 115 103 128 168 

(三)数组搜索

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

array<int>^ value = { 23, 45, 68, 94, 123, 150, 203, 299 };
int toBeFound = 127;
int position = Array::BinarySearch(value, toBeFound);
if(position<0)Console::WriteLine(L"{0} was not found.", toBeFound);
elseConsole::WriteLine(L"{0} was found at index position {1}", toBeFound, position);

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

array<int>^ value = { 23, 45, 68, 94, 123, 150, 203, 299 };
int toBeFound = 127;
int position = Array::BinarySearch(value, 3, 6, toBeFound);    
- - - - - - - - - - - - - - - - <<== 华丽的分割线 ::开始==>> [Ex4_14.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ex4_14.cpp : main project file.
#include "stdafx.h"
using namespace System;int main(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"};int result = 0;Array::Sort(names, weights);for each( String^ name in toBeFound ){result = Array::BinarySearch(names, name);if(result<0)Console::WriteLine(L"{0} was not found.", name);elseConsole::WriteLine(L"{0} weights {1} lbs.", name, weights[result]);}return 0;
}
- - - - - - - - - - - - - - - - <<== 华丽的分割线 ::开始==>> [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";
int position = Array::BinarySearch(names, name);
if(position<0)
position = ~position;

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

array<String^>^ newNames = gcnew array<String^>(names->Length+1);
for(int i=0;i<position;i++)newNames[i] = names[i];
newNames[position] = name;if(position<name->Length)for(int i=position; i<names->Length; i++)newNames[i+1] = names[i];
names = nullptr;

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

(四)多维数组

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

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

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

int nrows = 4;
int ncols = 5;
array<int, 2>^ value = gcnew array<int, 2>(nrows, ncols);
for(int i=0; i<nrows; i++)
    for(int j=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 : main project file.
#include "stdafx.h"
using namespace System;int main(array<System::String ^> ^args)
{const int SIZE = 12;array<int, 2>^ products = gcnew array<int, 2>(SIZE, SIZE);for(int i=0; i<SIZE; i++)for(int j=0; j<SIZE; j++)products[i, j] = (i+1)*(j+1);Console::WriteLine(L"Here is the {0} times table:", SIZE);// Write horizontal divider linefor(int i=0; i<=SIZE; i++)Console::Write(L"_____");Console::WriteLine();// Write top line of tableConsole::Write(L"    |");for(int i=1; i<=SIZE; i++)Console::Write("{0, 3} |", i);Console::WriteLine();// Write horizontal divider line with verticalsfor(int i=0; i<=SIZE; i++)Console::Write("____|", i);Console::WriteLine();// Write remaining linesfor(int i=0; i<SIZE; i++){Console::Write(L"{0, 3} |", i+1);for(int j=0; j<SIZE; j++)Console::Write("{0, 3} |", products[i, j]);Console::WriteLine();}// Write horizontal divider linefor(int i=0; i<=SIZE; i++)Console::Write("_____", i);Console::WriteLine();return 0;
}
- - - - - - - - - - - - - - - - <<== 华丽的分割线 ::结束==>> [Ex4_15.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

上面的例子创建了一个12x12的乘法表,输出如下:

Here is the 12 times table:
_________________________________________________________________|  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 |
_________________________________________________________________

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

const int SIZE = 12;
array<int, 2>^ products = gcnew array<int, 2>(SIZE, SIZE);

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

(五)数组的数组

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

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

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

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

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

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

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

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

- - - - - - - - - - - - - - - - <<== 华丽的分割线 ::开始==>> [Ex4_16.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ex4_16.cpp : main project file.
#include "stdafx.h"
using namespace System;int main(array<System::String ^> ^args)
{array<array<String^>^>^ grades = gcnew array<array<String^>^>{gcnew array<String^>{"Louise", "Jack"},gcnew array<String^>{"Bill", "Maray", "Ben", "Joan"},gcnew array<String^>{"Jill", "Will", "Phil"},gcnew array<String^>{"Ned", "Fred", "Ted", "Jed", "Ed"},gcnew array<String^>{"Dan", "Ann"}};wchar_t gradeLetter = 'A';for each(array<String^>^ grade in grades){Console::WriteLine(L"Students with Grade {0}:", gradeLetter++);for each(String^ student in grade)Console::Write("{0, 12}", student);Console::WriteLine();}return 0;
}
- - - - - - - - - - - - - - - - <<== 华丽的分割线 ::结束==>> [Ex4_16.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

输出为

Students with Grade A:Louise        Jack
Students with Grade B:Bill       Maray         Ben        Joan
Students with Grade C:Jill        Will        Phil
Students with Grade D:Ned        Fred         Ted         Jed          Ed
Students with Grade E:Dan         Ann
 
摘:http://www.cppblog.com/golq/archive/2009/06/29/88733.html
更多:http://www.cppblog.com/golq/category/11113.html

转载于:https://www.cnblogs.com/KivenLin/archive/2012/11/13/2768857.html

C++/CLI学习入门数组相关推荐

  1. 将MNIST手写数字数据集导入NumPy数组(《深度学习入门:基于Python的理论与实现》实践笔记)

    将MNIST手写数字数据集导入NumPy数组(<深度学习入门:基于Python的理论与实现>实践笔记) 一.下载MNIST数据集(使用urllib.request.urlretrieve( ...

  2. 深度学习入门之PyTorch学习笔记:深度学习框架

    深度学习入门之PyTorch学习笔记 绪论 1 深度学习介绍 2 深度学习框架 2.1 深度学习框架介绍 2.1.1 TensorFlow 2.1.2 Caffe 2.1.3 Theano 2.1.4 ...

  3. 深度学习入门|第5章 误差反向传播法(二)

    误差反向传播法 前言 此为本人学习<深度学习入门>的学习笔记 四.简单层的实现 本节将用 Python 实现前面的购买苹果的例子.这里,我们把要实现的计算图的乘法节点称为"乘法层 ...

  4. 使用TensorFlow.js在浏览器中进行深度学习入门

    目录 设置TensorFlow.js 创建训练数据 检查点 定义神经网络模型 训练AI 测试结果 终点线 内存使用注意事项 下一步是什么?狗和披萨? 下载TensorFlowJS示例-6.1 MB T ...

  5. 深度学习入门笔记(六):误差反向传播算法

    专栏--深度学习入门笔记 推荐文章 深度学习入门笔记(一):机器学习基础 深度学习入门笔记(二):神经网络基础 深度学习入门笔记(三):感知机 深度学习入门笔记(四):神经网络 深度学习入门笔记(五) ...

  6. 深度学习入门笔记(五):神经网络的学习

    专栏--深度学习入门笔记 推荐文章 深度学习入门笔记(一):机器学习基础 深度学习入门笔记(二):神经网络基础 深度学习入门笔记(三):感知机 深度学习入门笔记(四):神经网络 深度学习入门笔记(五) ...

  7. 深度学习入门笔记(四):神经网络

    专栏--深度学习入门笔记 推荐文章 深度学习入门笔记(一):机器学习基础 深度学习入门笔记(二):神经网络基础 深度学习入门笔记(三):感知机 深度学习入门笔记(四):神经网络 深度学习入门笔记(五) ...

  8. 给深度学习入门者的Python快速教程 - 番外篇之Python-OpenCV

    转载自:https://zhuanlan.zhihu.com/p/24425116 本篇是前面两篇教程:给深度学习入门者的Python快速教程 - 基础篇 给深度学习入门者的Python快速教程 - ...

  9. 给深度学习入门者的Python快速教程 - numpy和Matplotlib篇

    转载自:https://zhuanlan.zhihu.com/p/24309547 本篇部分代码的下载地址: https://github.com/frombeijingwithlove/dlcv_f ...

最新文章

  1. 测试可编程波形发生器 AD9833
  2. POJ 2823 Sliding Window(单调队列)
  3. Linux命令学习总结:dos2unix - unix2dos
  4. [转载] python字符串处理函数汇总
  5. pytorch---tensor概念的介绍
  6. Matlab画图和点标记
  7. 腾讯翻译君在线翻译怎么翻译整个文件_很实用的PDF文档在线翻译工具,整篇PDF翻译一键搞定...
  8. linux修改时区为UTC
  9. 排名下降 流量减少 动态评分 服务 违规降权后,如何恢复搜索权重?
  10. 揭秘:APP定制开发的完整流程
  11. BUUCTF-MISC-被劫持的神秘礼物~梅花香之苦寒来
  12. 2022推荐大宗商品行业研究报告投资策略产业发展前景市场行情分析(附件中为网盘链接,报告持续更新)
  13. 电脑更换硬盘 | 怎么迁移数据到新硬盘?
  14. 逆向初学者做题记录3.28
  15. 如何让自己像打王者荣耀一样发了疯、拼了命、失了智的学习?
  16. wordpressQQ登陆php代码_WordPress代码实现QQ账号互联授权登录注册功能 | 搁浅SEO
  17. java minma_Java Core.minMaxLoc方法代码示例
  18. 斐波那契问题——上台阶问题
  19. 一网打尽OkHttp中的缓存问题
  20. 【智能控制实验】基于MATLAB的BP神经网络设计

热门文章

  1. Leetcode --9
  2. 深度学习的实用层面 —— 1.3 机器学习基础
  3. a除b的余数【取模(%)与取余(/)的区别】
  4. Incorporating Lexical Priors into Topic Models(即交互式主题模型的应用)论文阅读
  5. JAVA--位移运算符详细分析【转载】
  6. Word2010双栏中某一处改变为单栏后,无法撤销还原为原来的双栏格式的解决方案
  7. 哈夫曼编码(Huffman Coding)
  8. Android studio 快速解决Gradle's dependency cache may be corrupt 和 Gradle配置 gradle-3.*-all.zip快速下载
  9. Unity 导出切片精灵
  10. toj 4317 多连块拼图