Compartilhar via


Declaração de um objeto de classe de referência do CLR

A sintaxe para declarar e instanciar um objeto de um tipo de classe de referência foi alterado a partir de Managed Extensions for C++ para Visual C++ 2010.

No Managed Extensions, um objeto de tipo de classe de referência é declarado usando a sintaxe de ponteiro ISO C++, com um uso opcional da __gc palavra-chave à esquerda da estrela (*). Por exemplo, eis uma variedade de referência declarações de objeto de tipo de classe em que a sintaxe de Managed Extensions:

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);
   }
};

Sob a nova sintaxe, declare um objeto de tipo de classe de referência usando um novo token declarativo (^) conhecido formalmente como um identificador de controle e mais informalmente como um hat. (O adjetivo controle significa que um tipo de referência fica no heap CLR e pode, portanto, movam locais durante a compactação de heap de coleta de lixo. Uma alça de controle transparente é atualizada durante o tempo de execução. Dois conceitos semelhantes são o a referência de rastreamento (%) e o ponteiro interior (interior_ptr<>), discutido no Semântica do tipo de valor.

Principais motivos para mover a sintaxe declarativa longe uma reutilização da sintaxe de ponteiro ISO C++ são:

  • O uso da sintaxe de ponteiro não permitiu que os operadores sobrecarregados para ser aplicada diretamente a um objeto de referência. Em vez disso, era preciso chamar o operador usando seu nome interno, como rV1->op_Addition(rV2) em vez de ser mais intuitiva rV1+rV2.

  • Um número de operações de ponteiro, como, por exemplo, a projeção e aritmética de ponteiro não permitido para objetos armazenados em um lixo coletado heap. A noção de uma alça de controle melhor captura a natureza de um tipo de referência do CLR.

O __gc modificador em uma alça de controle é desnecessário e não é suportado. O uso do próprio objeto não será alterado; ainda que ela acessa os membros através do operador de seleção de membro de ponteiro (->). Por exemplo, eis o exemplo de código anterior do Managed Extensions traduzido para a nova sintaxe:

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);   }
};

Alocação dinâmica de um objeto no Heap CLR

No Managed Extensions, a existência de dois new expressões alocar entre o heap nativo e gerenciado foi amplamente transparente. Em quase todos os casos, o compilador é capaz de usar o contexto para determinar se deve alocar memória do heap nativo ou gerenciado. For example,

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

Quando você não deseja que a alocação de heap contextual, você pode direcionar o compilador com um a __gc ou __nogc palavra-chave. Na sintaxe de novo, a natureza separada das duas novas expressões é explicitada com a introdução da gcnew palavra-chave. Por exemplo, as declarações de três anteriores examinar como segue a nova sintaxe:

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

Aqui está a inicialização de Managed Extensions da Form1 membros declarados na seção anterior:

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);
}

Aqui está a inicialização mesma recast para a nova sintaxe. Observe que o chapéu não é necessário para o tipo de referência, quando ele é o destino de um gcnew expressão.

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 );
}

Uma referência de rastreamento para nenhum objeto

Na nova sintaxe, 0 não representa mais um endereço nulo, mas é tratado como um inteiro, mesmo que 1, 10, ou 100. Um novo token especial representa um valor nulo para uma referência de rastreamento. Por exemplo, Managed Extensions inicializamos um tipo de referência para não atender a nenhum objeto da seguinte maneira:

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

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

Na nova sintaxe, qualquer inicialização ou a atribuição de um valor digite para um Object faz com que um boxing implícita de que tipo de valor. Na nova sintaxe, ambos obj e obj2 são inicializadas abordados in a box objetos de Int32, mantendo os valores 0 e 1, respectivamente. For example:

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

Portanto, para realizar a inicialização explícita, atribuição e comparação de uma alça de controle como null, use uma nova palavra-chave, nullptr. A revisão correta do exemplo original fica da seguinte maneira:

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

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

Isso complica um pouco a portabilidade do código existente para a nova sintaxe. Por exemplo, considere a seguinte declaração de classe de valor:

__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 [];
};

Aqui, ambos args e env são tipos de referência do CLR. A inicialização desses dois membros para 0 no construtor não permanecem inalteradas na transição para a nova sintaxe. Em vez disso, deve ser alterados para 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;
};

Da mesma forma, testa a esses membros compará-los para 0 também deve ser alterado para comparar os membros da nullptr. Aqui está a sintaxe de Managed Extensions:

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;
}

Aqui está a revisão, substituindo a cada 0 de instância com um nullptr. A ferramenta de conversão ajuda a essa transformação, automatizando muitos se não usam todas as ocorrências, inclusive o NULL macro.

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;
}

O nullptr é convertido em qualquer ponteiro ou tipo de identificador de controle, mas não é promovido a um tipo integral. Por exemplo, no seguinte conjunto de inicializações, o nullptr é válido apenas como um valor inicial para as duas primeiras.

// 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;

Da mesma forma, dado um conjunto sobrecarregado de métodos como, por exemplo, o seguinte:

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

Uma chamada com nullptr literal, como a seguir

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

é ambígua porque o nullptr corresponde a uma alça de controle e de um ponteiro e não há nenhuma preferência para um tipo a outro. (Essa situação requer uma conversão explícita para remover a ambigüidade).

Uma chamada com 0 exatamente a instância de correspondências (3):

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

porque 0 é do tipo inteiro. Foram f(int) não está presente, a chamada ambígua corresponderiam f(char*) por meio de uma conversão padrão. As regras de correspondência conceder a precedência de uma correspondência exata através de uma conversão padrão. Na ausência de uma correspondência exata, uma conversão padrão tem precedência sobre uma boxing implícito de um tipo de valor. É por isso que não há nenhuma ambigüidade.

Consulte também

Referência

Classes and Structs (Managed)

^ (Handle to Object on Managed Heap)

nullptr

Conceitos

Tipos gerenciados (C + + CL)