4.6.5 内部指针
虽然我们不能对跟踪句柄中的地址执行算术运算,但C++(www.cppentry.com)/CLI提供的另外一种指针形式却允许算术运算,那就是用关键字interior_ptr定义的内部指针。内部指针中存储的地址必要时可以由CLR垃圾回收机制自动更新。内部指针总是函数的局部自动变量。
下面是定义内部指针的方法,示例指针包含某数组中第一个元素的地址:
- array<double>^ data = {1.5, 3.5, 6.7, 4.2, 2.1};
- interior_ptr<double> pstart = &data[0];
我们必须在interior_ptr关键字后面的尖括号内指定内部指针指向的对象类型。在第二条语句中,正如使用本地C++(www.cppentry.com)指针时那样,我们使用&运算符以数组中第一个元素的地址初始化该指针。如果不给内部指针提供初值,则系统默认将其初始化为nullptr。数组总是在CLR堆上分配的,因此垃圾回收器可以调整这里定义的内部指针包含的地址。
在内部指针的类型指定方面有一些限制。内部指针可以包含堆栈上数值类对象的地址,也可以包含指向CLR堆上某个对象的句柄的地址,但不能包含CLR堆上整个对象的地址。内部指针还可以指向本地类对象或本地指针。
我们也可以使用内部指针,存储作为堆上对象组成部分的数值类对象(如CLR数组的元素)的地址。我们可以创建内部指针来存储System::String对象的跟踪句柄的地址,但不能创建内部指针来存储String对象本身的地址。例如:
- interior_ptr<String^> pstr1; // OK - pointer to a handle
- interior_ptr<String> pstr2; // Will not compile
- pointer to a String object
所有可应用于本地C++(www.cppentry.com)指针的算术运算也都可以应用于内部指针。我们可以通过递增或递减内部指针来改变其包含的地址,从而引用后面或前面的数据项;还可以使内部指针加上或减去某个整数,或者对内部指针进行比较。下面我们通过一个完整的示例来演示内部指针的用法。
试一试:创建并使用内部指针
本示例练习使用指向数值和字符串的内部指针:
- // Ex4_20.cpp : main project file.
- // Creating and using interior pointers
-
- #include "stdafx.h"
-
- using namespace System;
-
- int main(array<System::String ^> ^args)
- {
- // Access array elements through a pointer
- 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.0;
- while(pstart <= pend)
- sum += *pstart++;
-
- Console::WriteLine(L"Total of data array elements = {0}\n", sum);
-
- // Just to show we can - access strings through an interior pointer
- 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;
- }
该程序的输出如下:
- Total of data array elements = 18
- Land ahoy!
- Splice the mainbrace!
- Shiver me timbers!
- Never throw into the wind!
示例说明
在创建元素为double类型的数据数组之后,我们定义两个内部指针:
- interior_ptr<double> pstart = &data[0];
- interior_ptr<double> pend = &data[data->Length - 1];
第一条语句将pstart创建成指向double类型的指针,并用数组中第一个元素data[0]的地址将其初始化。内部指针pend被初始化为数组中最后一个元素data[data->Length-1]的地址。因为data->Length是数组中元素的数量,所以减去1就是最后一个元素的索引。
while循环累计数组中所有元素的总和:
- while(pstart <= pend)
- sum += *pstart++;
只要内部指针pstart包含的地址不大于pend包含的地址,该循环就继续执行。
在循环内部,pstart最初包含第一个数组元素的地址。*pstart表达式通过解除对该指针的引用获得第一个元素的值,结果与sum相加。然后,该指针包含的地址因++运算符而递增。在最后一次执行循环时,pstart包含最后一个元素的地址,与pend包含的地址值相同,这样递增pstart将使循环条件成为false,因为pstart已经大于pend。在循环结束以后,我们输出sum的值,以便可以确认while循环是否正确工作。
接下来,我们创建一个包含4个字符串的数组:
- array<String^>^ strings = { L"Land ahoy!",
- L"Splice the mainbrace!",
- L"Shiver me timbers!",
- L"Never throw into the wind!"
- };
然后,for循环将各个字符串输出到命令行上:
- for(interior_ptr<String^> pstrings = &strings[0] ;
- pstrings-&strings[0] < strings->Length ; ++pstrings)
- Console::WriteLine(*pstrings);
for循环条件中的第一个表达式声明内部指针pstrings,并将其初始化为strings数组中第一个元素的地址。决定for循环是否继续的第二个表达式是:
- pstrings-&strings[0] < strings->Length
只要pstrings包含有效数组元素的地址,pstrings中的地址与第一个数组元素的地址之间的差值就小于strings->Length表达式给出的数组元素的数量。因此,当这个差值等于数组的长度时,该循环结束。从输出中可以看出,一切都在按照我们预期的那样工作。
内部指针最为常见的用途是引用作为CLR堆对象组成部分的对象,我们将在本书后面更多地了解这一点。