Упаковка и распаковка примитивных типов данных
Упаковка и распаковка – важная концепция программирования в .NET вне зависимости от того, какой именно язык программирования вы используете. Одно из самых важных преимуществ .NET – унифицированная система типов. Каждый тип, в том числе простые упакованные встроенные типы, такие как _box (int), является потомком класса System.Object (Система.Объект). В языках, подобных Smalltalk, все типы являются объектами, но это приводит к неэффективности использования простых типов.
В стандартном C++ простые встроенные типы данных и объекты обрабатываются по-разному, – это повышает эффективность использования типов, но исключает возможность унификации системы типов. Управляемый C++ объединяет преимущества обоих подходов, используя прием, называемый упаковкой (boxing). Упаковка – преобразование типов значений, таких, как int или double (с удвоенной точностью), в ссылку на объект, хранимый в динамически распределяемой области памяти.
Упаковка производится с помощью ключевого слова _box. Распаковка – преобразование упакованного типа (хранимого в динамически распределяемой области памяти) в неупакованное значение (хранимое в стеке). Распаковка выполняется приведением типов.
Проиллюстрируем упаковку и распаковку следующим фрагментом кода:
int x = 5; // простой встроенный тип int _box int *po = _box(x); // упаковка x = *ро; // распаковывание
Ключевое слово _box создает в управляемой динамически распределяемой области памяти управляемый объект, инкапсулирующий копию выражения, имеющего тип значения. Под выражением, имеющим тип значения, подразумевается примитивный тип данных, такой как int, float (с плавающей точкой), double (с удвоенной точностью), или char (символ), либо тип значения, определенный как класс или структура и описанный с использованием ключевого слова _value (значение). Например, предопределенный управляемый тип _boxed_System_Int32 инкапсулирует упакованный int, a управляемый тип _boxed_ValueStruct – упакованный тип значения ValueStruct.
Эти странные названия типов (_boxed_System_Int32 и _boxed_ValueStruct) не обязательно будут встречаться в вашем исходном коде, но они показываются утилитой Ildasm.exe. Обратите внимание, что _box int * – альтернативное имя управляемого типа _boxed_System_Int32, a _box ValueStruct* – альтернативное имя управляемого типа _boxed_ValueStruct.
Если ключевое слово _box используется для создания управляемого объекта, сборщик мусора .NET будет автоматически освобождать память, используемую данным объектом. Это похоже на концепцию использования для примитивных типов интерфейсных классов, однако упаковка имеет более важное значение в среде .NET, чем в программировании на обычном C++. Это происходит из-за того, что объекты в C++ можно использовать и как значения, и как ссылочные типы, тогда как в среде .NET управляемые объекты всегда являются ссылочными типами (т.е. ссылками или указателями на объекты, хранимые в управляемой динамически распределяемой области памяти).
Доступ к типам значений осуществляется так же, как и доступ к неупакованным типам. В приведенном ниже коде это делается в присваивании plntBox = 50. Несмотря на то, что plntBox указывает на управляемый объект, разыменованный указатель используется так, как будто он является просто указателем на неупакованный тип int.
//BoxExample.срр #using <mscorlib.dll> using namespace System; // использовать пространство имен Система; _value struct ValueStruct { public: int i; }; // функция ожидает получить управляемый указатель на объект void ExpectManagedObjectPointer( _box ValueStruct* pManagedObject) { pManagedOb]ect › i = 20; // изменяет упакованную копию Console::WriteLine(pManagedObject › i); } // функция ожидает получить управляемый указатель на объект void ExpectBoxedPrimitivePointer(_box int* plntBox) { *pIntBox = 50; //изменяет упакованную копию примитивного типа Console::WriteLine(*рIntBox); } void main(void) { ValueStruct ValueStruct; // объект типа значение в стеке ValueStruct.i = 10; // изменяет оригинал распакованной копии Console::WriteLine(ValueStruct.i); _box ValueStruct* pManagedObject = _box(valueStruct); //_boxed_ValueStruct ExpectManagedObjectPointer(pManagedObject); pManagedObject › i = 30;* // изменяет упакованную копию Console::WriteLine(pManagedObject › i); int j; // тип значения – примитивный тип данных j = 40; // изменяет первоначальный распакованный // примитивный тип Console::WriteLine(j); _box int *p!ntBox = _box(j); // упакованный_System_Int32 ExpectBoxedPrimitivePointer(plntBox); }
Приведенная программа напечатает:
10 20 30 40 50