Поделиться через


Объявление объекта ссылочного класса в среде CLR

Синтаксис объявления и реализации объекта ссылочного типа класса изменился в Visual C++ 2010 по сравнению с управляемыми расширениями для C++.

В управляемых расширениях объект ссылочного типа класса объявляется с помощью синтаксиса указателя ISO-C++ и необязательного использования ключевого слова __gc, которое находится с левой стороны звезды (*). Например, ниже представлено несколько объявлений объектов ссылочных типов класса в синтаксисе управляемого расширения:

public __gc class Form1 : public System::Windows::Forms::Form {
private:
   System::ComponentModel::Container __gc *components;
   Button __gc *button1;
   DataGrid __gc *myDataGrid;   
   DataSet __gc *myDataSet;

   void PrintValues( Array* myArr ) {
      System::Collections::IEnumerator* myEnumerator = 
         myArr->GetEnumerator();

      Array *localArray;
      myArr->Copy(myArr, localArray, myArr->Length);
   }
};

В новом синтаксисе объект ссылочного типа класса объявляется с помощью нового декларативного маркера (^), который формально называется дескриптором отслеживания, а неформально как шапка (прилагательное "отслеживания" означает, что ссылочный тип находится в куче среды CLR и, следовательно, легко изменяет расположение при сжатии кучи сборщиком мусора). Дескриптор отслеживания прозрачно обновляется во время выполнения. Два похожих понятия: ссылочное отслеживание (%) и внутренний указатель (interior_ptr<>) рассматриваются в разделе Семантика типа значения.

Ниже приведены основные причины удаления декларативного синтаксиса при повторном использовании синтаксиса указателя ISO-C++:

  • Использование синтаксиса указателя не позволяет перегружать операторы, которые непосредственно обращаются к ссылочному объекту. Приходится вызывать оператор с помощью внутреннего имени, такого как rV1->op_Addition(rV2), а не с помощью более интуитивного rV1+rV2.

  • Определенное количество операций указателя, например приведение и арифметика указателя, не размещаются в сохраненных объектах кучи сборщика мусора. Представление дескриптора отслеживания лучше охватывает природу ссылочного типа среды CLR.

Модификатор __gc в дескрипторе отслеживания является необязательным и, следовательно, не поддерживается. Метод использования объекта не изменяется. Объект имеет доступ к членам с помощью оператора в выборке членов указателя (->). Например, ниже приведен пример кода предыдущих управляемых расширений, транслированных в новом синтаксисе:

public ref class Form1: public System::Windows::Forms::Form {
private:
   System::ComponentModel::Container^ components;
   Button^ button1;
   DataGrid^ myDataGrid;
   DataSet^ myDataSet;

   void PrintValues( Array^ myArr ) {
      System::Collections::IEnumerator^ myEnumerator =
         myArr->GetEnumerator();

      Array ^localArray;
      myArr->Copy(myArr, localArray, myArr->Length);   }
};

Динамическое размещение объекта в куче среды CLR

В управляемых расширениях наличие двух выражений new для размещения между собственной и управляемой кучей было в общем и целом прозрачно. В большей части случаев компилятор, используя контекст, может определить размещать ли память из собственной или управляемой кучи. Например:

Button *button1 = new Button; // OK: managed heap
int *pi1 = new int;           // OK: native heap
Int32 *pi2 = new Int32;       // OK: managed heap

Если не требуется размещать контекстно-зависимую кучу, можно направить компилятор с помощью ключевого слова __gc или __nogc. В новом синтаксисе различная природа двух новых выражений указана явно; этого удалось достичь введением ключевого слова gcnew. Например, предыдущие три объявления имеют в новом синтаксисе следующий вид:

Button^ button1 = gcnew Button;        // OK: managed heap
int * pi1 = new int;                   // OK: native heap
Int32^ pi2 = gcnew Int32; // OK: managed heap

Ниже представлена инициализация управляемыми расширениями членов Form1, объявленных в предыдущем примере:

void InitializeComponent() {
   components = new System::ComponentModel::Container();
   button1 = new System::Windows::Forms::Button();
   myDataGrid = new DataGrid();

   button1->Click += 
      new System::EventHandler(this, &Form1::button1_Click);
}

Ниже приведен тот же самый пример, но уже в новом синтаксисе. Обратите внимание, что "шляпа" не требуется для ссылочных типов, если они являются целью выражения gcnew.

void InitializeComponent() {
   components = gcnew System::ComponentModel::Container;
   button1 = gcnew System::Windows::Forms::Button;
   myDataGrid = gcnew DataGrid;

   button1->Click += 
      gcnew System::EventHandler( this, &Form1::button1_Click );
}

Ссылка с отслеживанием на несуществующий объект

В новом синтаксисе объект 0 не представляет более адрес "null", но обрабатывается как целое число, так же как 1, 10 или 100. Новый особый маркер представляет значение NULL для ссылки с отслеживанием. Например, в управляемых расширениях ссылочный тип с адресом на несуществующий объект инициализируется следующим образом:

// OK: we set obj to refer to no object
Object * obj = 0;

// Error: no implicit boxing
Object * obj2 = 1;

В новом синтаксисе инициализация или назначения типа значения в Object вызывает неявную упаковку-преобразование типа значения. В новом синтаксисе значения obj и obj2 инициализируются в упакованных объектах с адресом Int32, соответственно сохраняя значения 0 и 1. Пример.

// causes the implicit boxing of both 0 and 1
Object ^ obj = 0;
Object ^ obj2 = 1;

Таким образом, чтобы выполнить явную инициализацию, назначение и сравнение дескриптора отслеживания со значением NULL, используйте новое ключевое слово nullptr. Исправленная версия предыдущего примера, выполняющаяся без ошибок, представлена ниже:

// OK: we set obj to refer to no object
Object ^ obj = nullptr;

// OK: we initialize obj2 to a Int32^
Object ^ obj2 = 1;

Тем не менее, это вызывает некоторые трудности при переносе существующего кода в новый синтаксис. Например, рассмотрим объявление класса значений:

__value struct Holder {
   Holder( Continuation* c, Sexpr* v ) {
      cont = c;
      value = v;
      args = 0;
      env = 0;
   }

private:
   Continuation* cont;
   Sexpr * value;
   Environment* env;
   Sexpr * args __gc [];
};

Представлены ссылочные типы args и env в среде CLR. Инициализация двух членов в 0 в конструкторе изменяется при трансляции в новый синтаксис. Члены следует преобразовать в nullptr:

value struct Holder {
   Holder( Continuation^ c, Sexpr^ v )
   {
      cont = c;
      value = v;
      args = nullptr;
      env = nullptr;
   }

private:
   Continuation^ cont;
   Sexpr^ value;
   Environment^ env;
   array<Sexpr^>^ args;
};

Аналогичным образом, данные тестирования членов и сравнение их с 0 также необходимо изменить, чтобы иметь возможность сравнить члены с nullptr. Ниже представлен синтаксис управляемых расширений:

Sexpr * Loop (Sexpr* input) {
   value = 0;
   Holder holder = Interpret(this, input, env);

   while (holder.cont != 0) {
      if (holder.env != 0) {
         holder=Interpret(holder.cont,holder.value,holder.env);
      }
      else if (holder.args != 0) {
         holder = 
         holder.value->closure()->
         apply(holder.cont,holder.args);
      }
   }

   return value;
}

Ниже представлена новая версия с изменением с 0 на nullptr. Автоматизация позволяет программе трансляции преобразовать многие, если не все совпадения, в том числе и использование макроса NULL.

Sexpr ^ Loop (Sexpr^ input) {
   value = nullptr;
   Holder holder = Interpret(this, input, env);

   while ( holder.cont != nullptr ) {
      if ( holder.env != nullptr ) {
         holder=Interpret(holder.cont,holder.value,holder.env);
      }
      else if (holder.args != nullptr ) {
         holder = 
         holder.value->closure()->
         apply(holder.cont,holder.args);
      }
   }

   return value;
}

nullptr преобразуется в указатель или тип дескриптора отслеживания, но не повышается до типа интеграла. Например, в следующем наборе инициализаций nullptr является действительным только в том случае, если начальное значение относится к первым двум.

// OK: we set obj and pstr to refer to no object
Object^ obj = nullptr;
char*   pstr = nullptr; // 0 would also work here

// Error: no conversion of nullptr to 0 …
int ival = nullptr;

Аналогично в следующем примере представлен перегруженный набор методов:

void f( Object^ ); // (1)
void f( char* );   // (2)
void f( int );     // (3)

В следующем примере показан вызов nullptr с литералом,

// Error: ambiguous: matches (1) and (2)
f(  nullptr );

который является неоднозначным, поскольку значение nullptr совпадает для дескриптора отслеживания и указателя, и при этом предпочтение не отдается ни одному из указанных типов (эта ситуация требует явного приведение для устранения неоднозначности).

Вызов с 0 точно совпадает с экземпляром (3):

// OK: matches (3)
f( 0 );

поскольку 0 является типом с целым числом. Если бы f(int) не был представлен, вызов бы однозначно совпал бы со стандартным преобразованием f(char*). Правила совпадения оставляют приоритет за точным совпадением над стандартным преобразованием. При отсутствии точного совпадения стандартное преобразование получает приоритет над неявной упаковкой-преобразованием типа значения. Это объясняет отсутствие неоднозначности.

См. также

Ссылки

Classes and Structs (Managed)

^ (Handle to Object on Managed Heap)

nullptr

Основные понятия

Управляемые типы (C++/CL)