没有合适的资源?快使用搜索试试~ 我知道了~
首页CLR编程,CLR 原理,CLR 内幕
CLR编程,CLR 原理,CLR 内幕
4星 · 超过85%的资源 需积分: 1 37 下载量 50 浏览量
更新于2023-03-03
评论
收藏 327KB DOC 举报
CLR编程CLR的动态内存分配以不同的方式工作,CLR维护其独立于本地C++堆的内存堆。当我们不再需要在CLR堆上分配的内存时,CLR会自动将其删除。因此,我们不必在为CLR编写的程序中使用delete运算符。CLR还可以压缩内存堆,以避免不时产生的碎片。这样,CLR就消除了出现内存泄漏和内存碎片的可能性。CLR提供的堆管理和整理机制被称作垃圾回收(garbage collection)-- 垃圾即被抛弃的变量和对象,CLR管理的堆被称为可回收垃圾的堆(garbage-collected heap)。在C++/CLI程序中,使用gcnew运算符而非new运算符分配内存。前缀gc表明,我们是在可回收垃圾的堆上分配内存,而不是在一切整理工作都要编程人员负责的本地C++堆上。
资源详情
资源评论
资源推荐
4.6 C++/CLI 编程
4.6 C++/CLI 编程
CLR 的动态内存分配以不同的方式工作,CLR 维护其独立于本地 C++堆的内存堆。
当我们不再需要在 CLR 堆上分配的内存时,CLR 会自动将其删除。因此,我们不必在为
CLR 编写的程序中使用 delete 运算符。CLR 还可以压缩内存堆,以避免不时产生的碎片。
这样,CLR 就消除了出现内存泄漏和内存碎片的可能性。CLR 提供的堆管理和整理机制被
称作垃圾回收(garbage collection)-- 垃圾即被抛弃的变量和对象,CLR 管理的堆被称为
可回收垃圾的堆(garbage-collected heap)。在 C++/CLI 程序中,使用 gcnew 运算符
而非 new 运算符分配内存。前缀 gc 表明,我们是在可回收垃圾的堆上分配内存,而不是
在一切整理工作都要编程人员负责的本地 C++堆上。
CLR 的垃圾回收器能够将不再需要的对象删除,并释放它们所占用的内存。垃圾回收
器如何知道何时不再需要堆上的某个对象?答案非常简单,CLR 记录着每个引用堆内对象
的变量,如果所有变量都不包含某个对象的地址,则该对象就不可能在程序中再次被引用,
因此可以将其删除。
因为垃圾回收过程可能需要压缩堆的内存区域,以消除零碎的未用内存块,所以存储
在堆内的数据项的地址有可能改变。因此,我们不能在可回收垃圾的堆中使用普通的本地
C++指针,因为如果指向的数据的位置发生变化,则该指针将不再有效。我们需要的访问
堆内对象的方法是,应该在垃圾回收器重新安排堆内数据项的位置时,能够更新相应的地
址。在 CLR 程序中有两种方法可以提供这种功能:类似于本地 C++指针的跟踪句柄(亦简
称为句柄)和等价于本地 C++引用的跟踪引用。
4.6.1 跟踪句柄
4.6.1 跟踪句柄
跟踪句柄类似于本地 C++指针,但也有明显不同的地方。跟踪句柄确实存储着某个
地址,如果堆压缩过程中移动了句柄引用的对象,垃圾回收器将自动更新句柄包含的地址。
但是,我们不能像使用本地指针那样使用跟踪句柄执行地址的算术操作,跟踪句柄的强制
类型转换也是不允许的。
在 CLR 堆中创建的所有对象都必须被跟踪句柄引用。所有属于引用类类型的对象都存
储在堆中,因此为引用这些对象而创建的变量都必须是跟踪句柄。例如,String 类属于引
用类类型,因此引用 String 对象的变量必须是跟踪句柄。供数值类类型使用的内存默认是
在堆栈上分配的,但我们也可以通过使用 gcnew 运算符选择将数值存储在堆上。此刻又
是一个很好的机会来回忆一下第 2 章曾经提到的一点:在 CLR 堆上分配的变量-- 其中包括
所有 CLR 引用类型,都不能在全局作用域中声明。
声明跟踪句柄
我们通过将符号^(通常被称为"帽子")放在类型名称的后面来指定该类型的句柄。例如,
下面这条语句就声明了一个可以存储 String 类型对象的地址、名为 proverb 的跟踪句柄:
1. String^proverb;
该语句定义的 proverb 变量是 String^类型的跟踪句柄。当我们声明某个句柄时,系
统自动将其初始化为空值,因此该句柄将不引用任何对象。为显式地将某个句柄设置为空
值,我们可以像下面这样使用关键字 nullptr:
1. proverb=nullptr;//Sethandletonull
注意,我们在这里不能像使用本地指针时那样,使用 0 来表示空值。如果用 0 来初始
化句柄,则数值 0 将被转换为该句柄引用的对象的类型,而这个新对象的地址将被存入该
句柄中。
当然,我们可以在声明时显式地初始化句柄。下面这条语句定义的也是指向 String 对
象的句柄:
1. String^saying=L"IusedtothinkIwas
indecisivebutnowI'mnotsosure";
该语句在堆上创建一个包含等号右边字符串的 String 对象,该新建对象的地址被存入
saying 中。注意,字符串字面值的类型是 const wchar_t*,而非 String。定义 String
类的方式使这样的字面值可以用来创建 String 类型的对象。
下面这条语句给出为数值类型创建句柄的方法:
1. int^value=99;
该语句创建一个 int^类型的句柄 value,并将堆内该句柄指向的数值初始化为 99。
记住,我们创建的是一种指针,因此在没有解除引用的情况下 value 不能参与算术运算。
为了解除对跟踪句柄的引用,我们以与使用本地指针时相同的方式使用*运算符。例如,下
面这条语句就在算术运算中使用了跟踪句柄指向的值:
1. intresult=2*(*value)+15;
圆括弧内的表达式*value 访问该跟踪句柄指向的地址存储的整数,因此变量 result
的结果是 213。
注意,当我们在等号左边使用句柄时,不需要显式解除引用就可以用来存储结果,编
译器将替我们处理好一切。例如:
1. int^result=0;
2. result=2*(*value)+15;
在此首先创建一个指向数值 0 的句柄 result。注意,第一行将使编译器产生一条警告
消息,因为编译器认为我们可能打算将句柄初始化为空值,但这种写法是错误的。因为在
下一条语句中 result 出现在等号左边,而右边的结果是数值,所以编译器能够判定必须解
除对 result 的引用才能存储数值。当然,我们也可以将这条语句写成下面的显式形式:
1. *result=2*(*value)+15;
注意,仅当真正定义过 result 之后,这条语句才能工作。如果只是声明了 result,则
执行上述代码时将产生运行时错误。例如:
1. int^result;//Declarationbutnodefinition
2. *result=2*(*value)+15;//Errormessage-unhandledexception
第二条语句要解除对 result 的引用,这意味着被指向的对象已经存在。但实际上并不
然,所以程序将产生运行时错误。第一条语句是句柄 result 的声明,该句柄默认情况下被
设置为空值,而我们不能解除对空句柄的引用。如果我们不在第二条语句中显式解除对
result 的引用,则一切都将像预期的那样正常工作。因为等号右边表达式的结果是数值类
类型,所以其地址被存入 result 中。
4.6.2 CLR 数组(1)
4.6.2 CLR 数组(1)
CLR 数组与本地 C++数组不同。供 CLR 数组使用的内存是在可回收垃圾的堆上分配
的,但区别远不止这些。CLR 数组具有本地 C++数组所没有的内置功能,这一点我们很
快就将看到。我们必须使用关键字 array 指定数组变量的类型,还必须在 array 关键字后
面的尖括号内指定数组元素的类型,因此引用一维数组的变量使用的通用形式是
array<element_type>^。因为 CLR 数组是在堆上创建的,所以数组变量总是一个跟踪
句柄。下面是一个声明数组变量的例子:
1. array<int>^data;
数组变量 data 可以存储任何对元素类型为 int 的一维数组的引用。
在声明数组变量的同时,我们可以使用 gcnew 运算符创建 CLR 数组:
1. array<int>^data=gcnewarray<int>(100);
2. //Createanarraytostore100integers
该语句创建一个名为 data 的一维数组。注意,数组变量是跟踪句柄,因此在尖括号
内指定元素类型之后,不要忘记还必须写上^号。元素数量出现在数组类型说明后面的圆
括弧内,因此该数组包含 100 个元素,各个元素可以存储一个 int 类型的数值。
与本地 C++数组相同,CLR 数组中元素的索引值也是从 0 开始的,因此我们可以像
下面这样给 data 数组中的元素赋值:
1. for(inti=0;i<100;i++)
2. data[i]=2*(i+1);
该循环将数组元素的值设置为 2,4,6,…,直到 200。CLR 数组中的元素都是对象,
因此该循环是在数组中存储 Int32 类型的对象。当然,这些对象在算术表达式中就像普通
整数一样,因此说它们是对象的事实在那些场合下是透明的。
在前面的循环中,元素数量是以字面值形式出现的。更好的方法是像下面这样使用数
组的 Length 属性,该属性记录着元素的数量:
1. for(inti=0;i<data->Length;i++)
2. data[i]=2*(i+1);
我们使用->运算符来访问 Length 属性,因为 data 是跟踪句柄,就像指针一样工作。
Length 属性是 32 位的整数值,记录着元素的数量。如果需要,也可以通过 LongLength
属性得到 64 位的数组长度。
我们还可以使用 for each 循环,逐一处理数组中所有元素。
1. array<int>^values={3,5,6,8,6};
2. foreach(intiteminvalues)
3. {
4. item=2*item+1;
5. Console::Write("{0,5}",item);
6. }
在该循环内,item 依次引用 values 数组中的每个元素。循环体内的第一条语句使当
前元素的值乘以 2 再加 1。第二条语句在 5 字符宽的字段中以右对齐方式输出当前元素的
新值,因此该代码段产生的输出如下:
1. 711131713
数组变量可以存储任何等级(即维数,比如前面 data 数组的等级是 1)和元素类型相同
的数组的地址。例如:
1. data=gcnewarray<int>(45);
该语句新建一个包含 45 个 int 类型元素的一维数组,并将该数组的地址存入 data 中。
原来的数组被抛弃。
我们还可以通过为数组中各元素提供初值的方式来创建数组:
1. array<double>^samples={3.4,2.3,6.8,1.2,5.5,4.9.7.4,1.6};
该数组的大小取决于大括号内初值的数量-- 这里是 8,这些初值被顺序赋给各个元素。
当然,数组元素可以是任意类型,因此我们可以轻松地创建一个字符串数组:
1. array<String^>^names={"Jack","Jane",
"Joe","Jessica","Jim","Joanna"};
该数组的元素是用大括号内的字符串进行初始化的,字符串的数量决定着数组元素的
数量。String 对象是在 CLR 堆上创建的,因此数组元素属于跟踪句柄类型 String^。
如果我们声明数组变量时不进行初始化,那么在使用一列初值时必须显式地创建该数
组。例如:
1. array<String^>^names;//Declarethearrayvariable
2. names=gcnewarray<String^>{"Jack","
Jane","Joe","Jessica","Jim","Joanna"};
第二条语句创建数组,并用大括号内的字符串进行初始化。如果没有显式的 gcnew
限定,这条语句将不能编译。
我们可以使用 Array 类中定义的静态 Clear()函数,将数组中任意的连续数组元素清
零。使用类名可以调用静态函数,当我们详细介绍类时将学习关于静态函数的更多知识。
下面是 Clear()函数的用法示例:
1. Array::Clear(samples,0,samples->Length);
//Setallelementstozero
Clear()的第一个参数是要被清零的数组,第二个参数是要清除的第一个元素的索引值,
第三个参数是要清除的元素数量。因此,该语句将 samples 数组的所有元素都置为 0.0。
如果我们对某种跟踪句柄(如 String^)的数组应用 Clear()函数,则所有元素都被置为空值;
如果是元素为 bool 类型的数组,则所有元素都被置为 false。
现在该让 CLR 数组在示例程序中一试身手了。
试一试:使用 CLR 数组
在本示例中,我们将生成一个随机数数组,然后找出其中的最大值。代码如下:
1. //Ex4_13.cpp:mainprojectfile.
2.
3. //UsingaCLRarray
4. #include"stdafx.h"
5.
6. usingnamespaceSystem;
7.
8. intmain(array<System::String^>^args)
9. {
10. array<double>^samples=gcnewarray<double>(50);
11.
12. //Generaterandomelementvalues
13. Random^generator=gcnewRandom;
14. for(inti=0;i<samples->Length;i++)
15. samples[i]=100.0*generator->NextDouble();
16.
17. //Outputthesamples
18. Console::WriteLine(L"Thearraycontainsthefollowingvalues:");
19. for(inti=0;i<samples->Length;i++)
20. {
21. Console::Write(L"{0,10:F2}",samples[i]);
22. if((i+1)%5==0)
23. Console::WriteLine();
24. }
25.
26. //Findthemaximumvalue
27. doublemax=0;
28. foreach(doublesampleinsamples)
29. if(max<sample)
30. max=sample;
31. Console::WriteLine(L"Themaximumvalueinthearrayis{0:F2}",max);
32. return0;
33. }
4.6.2 CLR 数组(2)
剩余33页未读,继续阅读
lxming188
- 粉丝: 2
- 资源: 2
上传资源 快速赚钱
- 我的内容管理 收起
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
会员权益专享
最新资源
- 2023年中国辣条食品行业创新及消费需求洞察报告.pptx
- 2023年半导体行业20强品牌.pptx
- 2023年全球电力行业评论.pptx
- 2023年全球网络安全现状-劳动力资源和网络运营的全球发展新态势.pptx
- 毕业设计-基于单片机的液体密度检测系统设计.doc
- 家用清扫机器人设计.doc
- 基于VB+数据库SQL的教师信息管理系统设计与实现 计算机专业设计范文模板参考资料.pdf
- 官塘驿林场林防火(资源监管)“空天地人”四位一体监测系统方案.doc
- 基于专利语义表征的技术预见方法及其应用.docx
- 浅谈电子商务的现状及发展趋势学习总结.doc
- 基于单片机的智能仓库温湿度控制系统 (2).pdf
- 基于SSM框架知识产权管理系统 (2).pdf
- 9年终工作总结新年计划PPT模板.pptx
- Hytera海能达CH04L01 说明书.pdf
- 数据中心运维操作标准及流程.pdf
- 报告模板 -成本分析与报告培训之三.pptx
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论2