垃圾收集特性与类型系统[原文发表时间:2004年10月14日]

原文出处:http://pluralsight.com/blogs/hsutter/archive/2004/10/14/2786.aspx

 

    今天早上,Ivan Vecerina在新闻组上问了我一个有关C++.NET/CLI垃圾收集环境的问题:

    为了实现垃圾收集,C++/CLI类必须构建在平行、独立体系上。这是在C++里支持GC的必然要求么?   

    不对,不是C++/CLI,这是架构的基本要求:垃圾收集和类型无关。而应该与对象相关。

    在托管扩展里,GC和类型挂钩。因为在托管环境里,申明修饰符只有*,而没有引入其他特殊关键字。所以T*可能表示三种不同的含义之一,这取决于T的具体类型。比如,假设T是一个CLI引用类型,那么T*代表的是一个指向GC内存的指针[译注1]。将GC与数据类型直接关联,带来的后果是:程序员必须牢记,能用这里的*干什么事,并不代表用另一个地方的*能干同样的事,因为*修饰的可能是不同的类型,所以其含义是随之变化的[译注2]。想象一下让你编写一个模板,要求可用“*”正常操作任何类型的难度吧。这个问题在C++/CLI中已经得到了解决。

    C++/CLI里,GC与对象挂钩。目前,我们还没打算一次性就实现规划的全部功能。在现在的Beta版本里,你已经可以在栈上创建将任何类型的对象,不过gcnew只能操作托管类型,new只能操作本地类型。正式版里,我们会允许gcnew生成任何类型对象(包括本地类型。因此,付出底层代理的小代价,就能让本地类型对象获得GC能力),new也能生成任何类型(包括CLI类型)的对象。

    这是让人相当期待的功能。不过,Ivan对此持怀疑态度:

    不可能吧,在支持C++GC环境里,能像现在可以将任何类型的对象分配在栈里一样,将任何类型对象分配在GC堆里吗?

    当然了。这归功于我们引入的“gcnew”和“^”、“%[译注3]——让GC与类型系统实现正交,彼此独立实现,因此GC将适用于任何类型(即与类型无关)。

    现在,我想说的是,托管扩展让GC依赖于类型的做法是一个失误。当然,这是个让人可以理解的失误,因为CLI引用总是只能被分配在GC堆上,实际被引用的对象也是如此。但C++现在要增加可将对象放置在其他地方的语法。在正式版里,我们会支持将CLI引用放在栈上,当然实际被引用的对象仍然是在GC堆上(必须是这样);物理上来说放在栈上的是一个句柄(CLI对象引用),编译器知道对象的生命周期,会在适当时候调用析构器,GC除了它最擅长的事情——在适当时候回收内存空间,什么也不干。

    在此之下,就是实现细节了。毋庸置疑,从语义上将,C++程序员可以将任何对象放在栈上。

 

 

译注1

    比如:

    int *pi1 = new int;                     //本地堆,pi1是一个本地指针

    Int32 *pi2 = new Int32;            //托管堆,pi2是一个托管引用

    绝大多数情况下,编译器能够从上下文正确地确定所需的是本机堆还是托管堆。另外,还可以人工指定分配位置,即通过引入__gc__nogc两个关键字来告知编译器。

 

译注2

    在托管扩展里,指针和引用在一些地方无法执行类似的操作。比如:

    1、指针支持操作符重载,但在引用没有实现,必须通过调用它的方法来实现。

    2、有许多指针操作,例如类型强制转换和算术运算引用是无效的(因为引用代表的是GC堆上的对象,这些对象可能会因为垃圾回收而移动的)。

    这就造成,对于申明格式相似的指针和引用,在使用操作时行为不同,因此会给用户带来很多困惑和较大难度。

 

译注3

    托管扩展里,比较关键的有两个概念,一是对象分配位置(本地堆还是托管堆),二是指针和引用;其关联是,分配在本地堆的对象,用指针管理,分配在托管堆的对象,用引用管理。托管扩展里,从编程实现角度说,主要的问题是将指针和引用(或者说本地堆和托管堆)混淆不清,比如指针和引用依靠类型分辨,还可人工加入__gc/__nogc。这个当时的设计思想有很大关系,具体可以参看《将程序从托管扩展 C++ 迁移到 C++/CLI的“附录:推动修订版语言设计”。

    1gcnew新版C++/CLI中,去除了原来所有的双下划线关键字。在分配空间确定部分,则引入了新关键字gcnew,和老关键字new配合使用:

    int * pi1 = new int;                               //本机堆

    interior_ptr<Int32> pi2 = gcnew Int32; //GC堆(或CLI堆,即托管扩展里的托管堆)

    2^。“引用”有了更明确、深层的涵义,即“跟踪句柄”,不正式的名字是“帽子”。“*”专指传统C++层面的指针,不再和托管环境里的引用混杂。比如:

    Button^ button1 = gcnew Button;

    引用类型的对象位于GC堆里,其位置可以随着垃圾回收透明移动,即跟踪句柄同时被透明更新。

    %是新引入的^的逆向运算符,即取得被跟踪引用对象的地址。^%的关系,类似于*&

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值