扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
学习 C++/CLI 的第二个要点是学习我们选择直接提供给程序员操作的那些底层 CLI 元素。例如, CLI 为所有语言都提供了垃圾收集服务。一门语言不能选择是否支持垃圾收集,而只能选择如何更好地提供该服务。
在 CLI 中,一个引用类型的所有对象都只能被分配在 CLI 托管堆上。这意味着 C++/CLI 支持两种动态堆——本地堆(没有任何形式的自动内存回收机制),和 CLI 托管堆。对于这两种动态堆,开发人员通常要用某种形式的 new 操作符来分配对象;如果操作成功,对象在堆中初始位置的地址将被返回。但是两者又有所区别,这是因为 CLI 托管堆中对象的位置有可能在垃圾收集器的清除以及随后的压缩中被重新调整。如果一个对象的位置被重新调整,那么 CLI 运行时中所含的其中一项服务会透明地更新所有引用该对象的指代品( thingee )。这就使得我们面临着一种困难的选择:我们是将这些指代品称为指针,并且继续用指针的语法来表示它们呢?还是引入一种新的类似的语法来表示它们需要特殊的处理?我们最后决定采用后者,看下面的代码:
N *pn = new N;
R ^rn = gcnew R;
这里, N 表示一个本地类型,而 R 表示一个 CLI 引用类型,帽子状的符号( ^ )表示相关的地址是一个托管堆上的追踪句柄( tracking handle )——也就是说,对象位置的任何重新调整都会被 CLI 所追踪,相应的句柄也会被透明地更新。其中关键字 gcnew 在这里被用作与 CLI 托管堆打交道的 new 表达式。
值类型事实上也可以位于托管堆上,虽然这并非必须。当它们作为一个引用类型的成员时,就会出现这种情况。如果我们允许获取一个引用类型内部成员的地址,那么本地指针也是不合适的,因为这些成员的位置也需要被追踪。一种解决方法是简单地禁止该项功能。这样语言当然会变得更加简单,但是同时语言也会变得更弱——例如我们将不能通过增长元素的地址值来遍历 CLI 数组,这是因为 CLI 数组是一个引用类型,其内的元素都位于托管堆上。不提供这样的功能意味着 CLI 数组将不能适用于标准模板库( STL )中的 iterator 模式以及泛型算法。对于一个 C++ 程序员来说,这是不可接受的。
支持获取可能位于托管堆中的值类型的地址同样需要引入一种追踪指针,我们称之为追踪内部指针( tracking interior pointer )。另外,我们还支持追踪引用( tracking reference )这样的概念——它具有类似本地引用的别名语义,但是它会在必要的时候被 CLI 透明地更新。最后,我们还支持一种固定指针( pinning pointer )的概念,它可以在该指针的作用范围内阻止垃圾收集器移动其所引用的对象。
这些新的符号及其表示的复杂的间接类型是在我们对托管堆反复学习和认识之后产生的。面对生存期短暂的托管堆对象,我们需要某种精巧的方式来认识和使用它们,我们相信这些额外的间接类型可以给大家很多帮助。我们将在今后的专栏文章中详细讨论它们。
我们在此对一门 CLI 语言所选择的第二个设计层面表示了其对底层 CLI 实现模型的一层映射。选择什么样的映射取决于该编程语言定位于什么样的程序及程序员模型。当你选择一门 CLI 语言进行编程的时候,你实际上也是在选择遵从一种程序员模型。我们对于 C++/CLI 程序员的定位是那些历练较深的系统程序员,这些程序员通常所面对的任务是为高层的商业逻辑提供基础性的构造和关键性的应用,这时候她就必须要同时考虑系统的扩展性和性能,因此必须对底层 CLI 有一个系统级的视角。
查看本文来源如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者