9.9 C++(www.cppentry.com)/CLI编程(www.cppentry.com)
包括用户定义的类在内的所有C++(www.cppentry.com)/CLI类,默认情况下都是派生类,因为数值类和引用类都是以System::Object这个标准类作为基类的。这意味着数值类和引用类都继承了System::Object类,因此都具有System::Object类的功能。因为ToString()函数在System::Object类中被定义为虚函数,所以我们可以在自己的类中重写该函数,并在需要时多态地调用该函数。这正是我们在前面几章定义类的ToString()函数时一直在做的事情。
因为System::Object是所有C++(www.cppentry.com)/CLI类的基类,所以句柄类型System::Object^起到了与本地C++(www.cppentry.com)中可用来引用任何类型的对象的void*类型相同的作用。
9.9.1 装箱与拆箱
供所有数值类类型使用的System::Object基类还负责实现基本类型数值的装箱(boxing)和拆箱(unboxing)操作。数值类型实例的装箱将它转换为垃圾回收堆上的一个对象,因此它会与基本数值一起承载完整的类型信息。拆箱是装箱的逆向操作。装箱/拆箱功能意味着基本类型的数值可以表现对象的行为,但参与数值运算时又能抛掉对象应有的系统开销。基本类型的数值只是作为用于常规运算的数值存储在堆栈上,当它们需要表现出对象的行为时,只能被转换为堆上由System::Object^类型的句柄引用的对象。例如,如果我们将一个拆箱的数值通过一个适当数值类类型的形参传递给某函数,那么编译器会将该数值转换为堆上的对象;这一步通过在包含该值的堆上新建一个对象来完成。因此,我们实现了隐式装箱,实参值会被自动装箱。
当然,也可以进行显式装箱。我们可以通过将数值赋给一个Object^类型的变量来强制装箱。例如:
- double value = 3.14159265;
- Object^ boxedValue = value;
第二个语句强制装箱value,装箱后的表示由句柄boxedValue引用。
我们也可以用gcnew强制装箱一个数值,以便在垃圾回收堆上创建一个装箱过的数值,例如:
- long^ number = gcnew(999999L);
这个语句隐式装箱数值999999L,并将它存储在堆上句柄number所引用的位置中。
我们可以用解引用运算符对数值类型执行拆箱操作,例如:
- Console::WriteLine(*number);
句柄number所指向的数值被拆箱,然后作为一个数值传递给WriteLine()函数。
最后我们可以用safe_cast对一个已装箱的数值执行拆箱操作:
- long n = safe_cast<long>(number);
该语句对number拆箱并将数值存储在n中。注意,如果没有safe_cast,这个语句就不能编译,因为在这种情况下没有发生隐式转换。