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 de Managed Extensions for C++ para Visual C++.

Em gerenciado extensões, um objeto de tipo de classe de referência é declarado usando a sintaxe de ponteiro ISO C++, com um uso opcional do __gc palavra-chave à esquerda da estrela (*).Por exemplo, eis uma variedade de referência declarações de objeto de tipo de classe em sintaxe 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.(Adjetivo controle significa que um tipo de referência fica no heap CLR e pode, portanto, mover de modo transparente locais durante a compactação de heap de coleta de lixo.Uma alça de controle é atualizada imperceptivelmente em tempo de execução.São dois conceitos semelhantes a referência de rastreamento (%) e o ponteiro interior (interior_ptr<>), discutido no Semântica de tipo de valor.

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

  • O uso da sintaxe de ponteiro não permitiu que 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 projeção e aritmético 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 é alterado; ainda que ele acesse membros através do operador de seleção de membro de ponteiro (->).Por exemplo, aqui está o exemplo de código anterior 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

Em extensões gerenciadas, a existência de dois new expressões alocar entre o heap gerenciado e nativo 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.Por exemplo,

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

Quando não quiser que a alocação de heap contextual, pode direcionar o compilador com um a __gc ou __nogc palavra-chave.Na sintaxe de novo, a natureza separada de duas expressões de novas é explicitada com a introdução do gcnew palavra-chave.Por exemplo, as declarações de três anteriores examinar a seguinte 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 Managed Extensions do 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 que recast a nova sintaxe.Observe que o chapéu não é necessário para o tipo de referência quando é 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 a nenhum objeto de rastreamento

Na sintaxe de novo, 0 não representa 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, extensões gerenciadas, inicializamos um tipo de referência a nenhum objeto de endereço como segue:

// 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 atribuição de um valor digite para um Object faz com que uma conversão boxing implícita do tipo de valor.Na sintaxe de novo, ambos obj e obj2 inicializado abordados box objetos Int32 mantendo os valores 0 e 1, respectivamente.Por exemplo:

// 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 para null, usar uma nova palavra-chave, nullptr.A revisão correta do exemplo original é como segue:

// 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 os membros comparando-as 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 é a revisão, substituindo cada 0 de instância com um nullptr.A ferramenta de tradução ajuda a essa transformação, automatizando várias se não todas as ocorrências, incluindo usam 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 tipo de identificador de ponteiro ou controle, mas não é promovido para um tipo integral.Por exemplo, no seguinte conjunto de inicializações, o nullptr é válida somente como um valor inicial para os dois primeiros.

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

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 um ponteiro e não há nenhuma preferência para um tipo a outro.(Essa situação requer uma conversão explícita a ambigüidade).

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

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

porque 0 é do tipo número inteiro.Foram f(int) não estiver presente, a chamada sem ambigüidade corresponderia f(char*) por meio de uma conversão padrão.Regras de correspondência dar prioridade uma correspondência exata através de uma conversão padrão.Ausência de uma correspondência exata, uma conversão padrão tem precedência sobre uma conversão boxing implícita de um tipo de valor.É por isso que não há nenhuma ambigüidade.

Consulte também

Referência

Classes e Estruturas (Extensões de Componentes C++)

Operador Handle to Object (^) (Extensões de Componentes C++)

nullptr (Extensões de Componentes C++)

Conceitos

Tipos gerenciados (C + + CL)