最近看了看C++/CLI specification。
的确,C++/CLI让C++在使用.NET的时候手感好了很多,一点学习小记,说的不对,各位多包涵。
Handle Type
对于CLI中的任意一个类型T,T^声明了一个T的handle类型,它用来指向创建在CLI Heap上的对象。由于创建载托管堆上的对象的位置有可能被运行时改变,因此,一个Handle类型可以动态跟踪其指向的对象。从某种意义上说,我们可以把Handle类型看作是托管堆上的指针,而把native pointer看作是原生堆上的指针。
一个handle类型变量的默认值是nullptr。
通过gcnew关键字,你可以在托管堆上创建一个CLI对象,这样的对象只能够通过handle来访问。例如:
R^ r1 = gcnew R; // Allocate an Object on the CLI heap
R^ r2 = r1; // Handles r1 and r2 refer to the same object
除非使用delete或者显式调用一个CLI对象的析构函数,否则CLI对象的析构函数绝对不会被调用。但是,当程序结束的时候,GC会回收对象的内存,并且如果一个对象有finalizer,这个东西倒是会被调用。例如:
ref
class T {
public
:
T() { }
~T() {
System::Console::WriteLine("I'm destructor!"); }
!T() {
System::Console::WriteLine("I'm finalizer!");
}
};
之后:
{
T^ t1 = gcnew T();
}
会看到只有finalizer被调用了,而:
{
delete t;
}
会看到析构函数被调用了。也就是说,对于一个托管对象,有2种清理方式。一种是通过析构函数进行确定性的清除;另一种是让CLI调用类对象的finalizer。
l
和原生指针不同,handle类型的变量具有跟踪功能,也就是说一个handle类型的变量可以根据其指向的CLI heap中对象的位置而改变(因为GC可能会移动托管堆中的对象)。也就是说:handle类型不能转换成void*,也不能进行相反的转换
l
handle
类型不能转换成整数类型,也不能进行相反的转换
l
不能对handle类型进行排序
l
Handle
类型的变量只能指向CLI heap中的对象
例如:
R^ r4 = new R;
Object^ o = r4;// OK
R^ r5 = dynamic_cast(o); // OK r4 and r5 point to the same object
long
l = reinterpret_cast<long>(r5); //error, can't convert to integer
R^ r6 = reinterpret_cast(l); // error, can't convert from integer
std::set s; // error, R^ can't be compared with less
所有指向同一个托管对象的引用都可以被看作是等价的,即使对象被GC移动了也是如此。一个handle可以有任意的生存周期。也可以被指定成nullptr。对于一个handle类型的变量,你也可以通过*来或得其指向的对象的引用。
Reference Type
一个原生引用可以被邦定到任何做为左值的原生变量(lvalue)上。
作为一个托管堆中的对象,由于GC有可能移动它的位置,因此它的位置必须被跟踪。因此,对于这样的对象的引用被称为tracking reference(%),由于存在gc-lvalue和lvalue的隐式转换,所以一个track reference可以既可以邦定到gc-lvalue,也可以邦定到lvalue。当其邦定到一个lvalue时,取其地址获得的是一个原生指针,否则获得的就是一个托管handle。例如:
R^ h = gcnew R; // allocate on CLI heap
R& r = *h; // bind tracking reference to ref class obj
void
F(V% r);
F(*gcnew V); // bind tracking reference to value class obj
N* p = new N();
N% rn = *p; // bind to native object;
和一个普通的引用一样,一个tracking reference也是不可以重邦定的。一旦在声明时被指定了对象,就不可以再进行更改。另外你也只可以把tracking reference类型的变量定义成automatic型的。
如果一个tracking reference被邦定到某个值类型的基类,那么这个tracking reference便不能邦定到这个派生的值类型。也就是说一个System::Object%不可以邦定到一个System::ValueType对象上。