与本地C++自己维护堆不同,C++/CLI中动态分配的内存是由CLR来维护的。当不需要堆时,CLR自动将其删除回收,同时CLR还能自动地压缩内存堆以避免产生不必要的内存碎片。这种机制能够避免内存泄露内存碎片,被称为垃圾回收,而由CLR管理的这种堆被称为CLR堆。它由操作符gcnew创建。

由于垃圾回收机制会改变堆中对象的地址,因此不能在CLR堆中使用普通C++指针,因为如果指针指向的对象地址发生了变化,则指针将不再有效。为了能够安全地访问堆对象,CLR提供了跟踪句柄(类似于C++指针)和跟踪引用(类似于C++)引用。

一、跟踪句柄

跟踪句柄类似于本地C++指针,但能够被CLR垃圾回收器自动更新以反映被跟踪对象的新地址。同时不允许对跟踪句柄进行地址的算术运算,也不能够进行强制类型转换。

凡是在CLR堆上创建的对象必须被跟踪句柄引用,这些对象包括:(1)用gcnew操作符显示创建在堆上的对象;(2)所有的引用数据类型(数值类型默认分配在堆栈上)。注意:所有分配在堆上的对象都不能在全局范围内被创建。

关于跟踪句柄的相关使用方法参见《C++/CLI学习入门(三):数组

二、跟踪引用

跟踪引用类似于本地C++引用,表示某对象的别名。可以给堆栈上的值对象、CLR堆上的跟踪句柄创建跟踪引用。跟踪引用本身总是在堆栈上创建的。如果垃圾回收移动了被引用的对象,则跟踪引用将被自动更新。

跟踪引用用%来定义,下面的例子创建了一个对堆栈上值对象的跟踪引用:

int value = 10;
int% trackValue = value;

stackValue为value变量的引用,可以用stackValue来访问value:

trackValue *= 5;
Console::WriteLine(value);	// Result is 50

三、内部指针

C++/CLI还提供一种用关键字interior_ptr定义的内部指针,它允许进行地址的算术操作。必要时,该指针内存储的地址会由CLR垃圾回收自动更新。注意,内部指针总是函数的局部自动变量。

下面的代码定义了一个内部指针,它含有某数组中第一个元素的地址:

array<double>^ data = {1.5, 3.5, 6.7, 4.2, 2.1};
interior_ptr<double> pstart = %data[0];

必须给interio_ptr指定内部指针指向的对象类型。此外还应该给指针进行初始化,如果不提供初始值,系统将其默认初始化为nullptr。

内部指针在指定类型时应注意:可以包含堆栈上值类型对象的地址,也可以包含指向CLR堆上某对象句柄的地址,还可以是本地类对象或本地指针,但不能是CLR堆上整个对象的地址。也就是说,可以使用内部指针存储作为CLR堆上对象组成部分的数值类对象(如CLR数组元素)的地址,也可以存储System::String对象跟踪句柄的地址,但不能存储String对象本身的地址。

interior_ptr<String^> pstr1;	// OK -- pointer to a handle
interior_ptr<String>  pstr2;	// ERROR -- pointer to a String object

与本地C++指针一样,内部指针可以进行算术计算。可以通过递增或递减来改变其包含的地址,从而引用后面或前面的数据项;还可在内部指针上加上或减去某个整数;可以比较内部指针。下面的例子展示了内部指针的用法:

- - - - - - - - - - - - - - - - <<== 华丽的分割线 ::开始==>> [Ex4_19.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Ex4_19.cpp : main project file.
#include "stdafx.h"
using namespace System;

int main(array<System::String ^> ^args)
{
	array<double>^ data = {1.5, 3.5, 6.7, 4.2, 2.1};
	interior_ptr<double> pstart = &data[0];
	interior_ptr<double> pend = &data[data->Length - 1];
	double sum = 0;

	while(pstart<=pend)
		sum += *pstart++;

	Console::WriteLine(L"Total of data array elements = {0}\n", sum);

	array<String^>^ strings = { L"Land ahoy!",
		L"Splice the mainbrace!",
		L"Shiver me timbers!",
		L"Never throw into the wind!"
	};

	for(interior_ptr<String^> pstrings = &strings[0]; pstrings-&strings[0] < strings->Length; ++pstrings)
		Console::WriteLine(*pstrings);

    return 0;
}
- - - - - - - - - - - - - - - - <<== 华丽的分割线 ::结束==>> [Ex4_19.cpp] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

输出为

Total of data array elements = 18

Land ahoy!
Splice the mainbrace!
Shiver me timbers!
Never throw into the wind!