Semântica de tipo de valor
Semântica de tipo de valor mudou de gerenciado Extensions para C++ para Visual C++ 2008.
Este é o tipo de valor canônico simples usado na gerenciado Extensions para C++ spec:
__value struct V { int i; };
__gc struct R { V vr; };
gerenciado Extensions, pode ter quatro variantes sintático de um tipo de valor (onde formulários 2 e 3 são as mesmas semanticamente):
V v = { 0 }; // Form (1)
V *pv = 0; // Form (2) an implicit form of (3)
V __gc *pvgc = 0; // Form (3)
__box V* pvbx = 0; // Form (4) must be local
Form (1) é o objeto de valor canônico e ele é razoavelmente bem compreendido, exceto quando alguém tenta chamar um método virtual herdado, sistema autônomo ToString(). Por exemplo:
v.ToString(); // error!
Para invocar esse método, porque não é substituído no V, o compilador deve ter acesso à tabela virtual associada da classe base. Como os tipos de valor são em estado armazenamento sem o ponteiro associado à sua tabela virtual (vptr), isso requer que v ser in a box. No design de linguagem de gerenciado Extensions, conversão boxing implícito não é compatível, mas deve ser especificado explicitamente pelo programador, sistema autônomo em
__box( v )->ToString(); // Managed Extensions: note the arrow
A principal motivação por trás desse design é pedagogical: mecanismo de base deve ser visível para o programador para que ela compreendam o 'custo' não fornece uma instância dentro de seu tipo de valor. ForamV para conter uma instância de ToString, a conversão boxing não seria necessário.
A complexidade léxica de conversão boxing explicitamente o objeto, mas não o custo subjacente de conversão boxing, é removida a nova sintaxe:
v.ToString(); // new syntax
mas ao custo de equivocado possivelmente o designer de classe sistema autônomo o custo de não ter fornecido uma instância explícita do ToString método dentro V. Preferência ao conversão boxing implícito a razão é que, embora geralmente seja apenas uma Designer de Classe, há um número ilimitado de usuários, nenhum dos quais seriam tem a liberdade de modificar V para eliminar a caixa possivelmente onerosa explícita.
O critérios pelo qual determinar se deve ou não fornecer uma instância de substituição de ToString dentro de um valor classe deve ser a freqüência e local dos seus usos. Se é chamado muito raramente, é claro que há poucas vantagens de sua definição se.Da mesma forma, se ele for chamado em áreas não alto desempenho do aplicativo, adicioná-lo também não concretamente adicionará para o desempenho geral do aplicativo.Como alternativa, um pode manter uma alça de acompanhamento para o valor in a box e chamadas por meio desse identificador não requer conversão boxing.
Outra diferença com um tipo de valor entre extensões gerenciadas e a nova sintaxe é a remoção do suporte para um construtor padrão.Isso ocorre porque há ocasiões em que durante a execução em que o CLR possa criar uma instância do tipo de valor sem chamar o construtor padrão associado.Ou seja, a tentativa de oferecer suporte a um construtor padrão dentro de um tipo de valor em gerenciado Extensions poderia não na sessão prática ser garantida.Devido a ausência de garantia, foi sentido ser melhor soltar o suporte totalmente em vez de tê-lo a ser não-determinística em seu aplicativo.
Isso não é tão ruim sistema autônomo inicialmente pode parecer.Isso acontece porque cada objeto de um tipo de valor é zerado automaticamente (isto é, cada tipo é inicializado com o valor padrão).sistema autônomo resultado, sistema autônomo membros de uma instância local nunca são indefinidos.Nesse sentido, a perda de capacidade de definir um construtor padrão trivial realmente não é uma perda em todos os – e na verdade é mais eficiente quando executado pelo CLR.
O problema é quando um usuário de gerenciado Extensions define um construtor padrão não-comum.Isso tem nenhum mapeamento para a nova sintaxe.O código dentro do construtor precisará ser migrado para um método de inicialização nomeado que, em seguida, teria que ser explicitamente chamado pelo usuário.
A declaração de um objeto do tipo de valor dentro da nova sintaxe caso contrário, é inalterada.O lado para baixo disso é que não são satisfatórios para quebra automática de tipos de valor nativo tipos pelos seguintes motivos:
Não há nenhum suporte para um destruidor dentro de um tipo de valor.Ou seja, não é possível automatizar um conjunto de ações acionadas por participante da tempo de vida do objeto.
A nativo classe pode estar contido somente em um tipo gerenciado sistema autônomo um ponteiro que é alocado, em seguida, no nativo heap.
Gostaríamos de encapsular um pequeno nativo classe em um tipo de valor em vez de um tipo de referência para evitar uma alocação de heap duplo: a pilha nativa para Isenção o tipo nativo e o heap CLR para Isenção wrapper gerenciado.Quebra automática de uma classe nativa dentro de um tipo de valor permite que você evite o gerenciado de pilha, mas não oferece nenhuma maneira de automatizar a recuperação de memória heap nativa.Tipos de referência são do tipo gerenciado practicable somente dentro do qual quebrar não trivial nativo classes.
Form (2) e Form (3) acima podem endereço praticamente qualquer coisa neste mundo ou o próximo (ou seja, nada gerenciada ou nativa). Assim, por exemplo, todos os itens a seguir é permitida no gerenciado Extensions:
__value struct V { int i; };
__gc struct R { V vr; };
V v = { 0 }; // Form (1)
V *pv = 0; // Form (2)
V __gc *pvgc = 0; // Form (3)
__box V* pvbx = 0; // Form (4)
R* r;
pv = &v; // address a value type on the stack
pv = __nogc new V; // address a value type on native heap
pv = pvgc; // we are not sure what this addresses
pv = pvbx; // address a boxed value type on managed heap
pv = &r->vr; // an interior pointer to value type within a
// reference type on the managed heap
So, a V* podem endereçar um local dentro de um bloco local (e, portanto, pode ser dangling), no escopo global, o nativo de pilha (por exemplo, se o objeto aborda já tiver sido excluído) dentro do heap CLR (e portanto serão rastreadas se devem ser realocado durante a coleta de lixo) e dentro do interior de um objeto de referência no heap CLR (um ponteiro de interior, sistema autônomo isso é chamado, é também transparentemente controlado).
Managed Extensions, não é possível separar os aspectos nativo de um V*; ou seja, é tratada em inclusive, que lida com a possibilidade de que ele endereçamento de um objeto ou subobjeto no heap gerenciado.
Na sintaxe de novo, um ponteiro de tipo de valor é fatorado em dois tipos: V*, que é limitado aos locais de heap CLR não e o ponteiro de interior, interior_ptr<V>, que permite, mas não requer um endereço dentro do heap gerenciado.
// may not address within managed heap
V *pv = 0;
// may or may not address within managed heap
interior_ptr<V> pvgc = nullptr;
Form (2) e Form (3) do MAP de gerenciado Extensions para interior_ptr<V>. Form (4) é um identificador de acompanhamento. Ele aborda todo o objeto que tenha sido in a box no heap gerenciado.Ela é convertida na nova sintaxe em um V^,
V^ pvbx = nullptr; // __box V* pvbx = 0;
As seguintes declarações in gerenciado Extensions todos mapeiam para interiores ponteiros na nova sintaxe.(Eles são tipos de valor dentro de System namespace).
Int32 *pi; // => interior_ptr<Int32> pi;
Boolean *pb; // => interior_ptr<Boolean> pb;
E *pe; // => interior_ptr<E> pe; // Enumeration
sistema autônomo tipos internos não são considerados tipos gerenciado, embora servem sistema autônomo aliases para sistema autônomo tipos de dentro de System espaço para nome. Assim, os seguintes mapeamentos verdadeiras entre gerenciado Extensions e a nova sintaxe:
int * pi; // => int* pi;
int __gc * pi2; // => interior_ptr<int> pi2;
Ao traduzir um V* no seu programa existente, a estratégia mais conservador é sempre ativá-lo um interior_ptr<V>. Isso é como ele foi tratado em gerenciado Extensions.Na sintaxe de novo, o programador tem a opção de restringir um tipo de valor para endereços de pilha não-gerenciado, especificando V* em vez de um ponteiro interior. Se, em traduzir seu programa, você pode fazer um fechamento transitivo de todos sistema autônomo seus usos e certifique-se de que nenhum endereço atribuído está dentro do heap gerenciado, deixando-o sistema autônomo V* foi estabelecida.
O coletor de lixo, opcionalmente, pode mover objetos que residem no heap CLR a locais diferentes dentro do heap, geralmente durante uma fase de compactação.Essa movimentação não é um problema de acompanhamento alças, referências de acompanhamento e ponteiros interiores que atualizar essas entidades transparente.Essa movimentação é um problema, no entanto, se o usuário tiver passado o endereço de um objeto no heap CLR fora do ambiente de tempo de execução.Nesse caso, o movimento do objeto volátil provavelmente causará uma falha de tempo de execução.A isenção objetos, sistema autônomo elas sejam movidas, nós deve fixá-los localmente para o local para a extensão de seu uso externo.
Nas extensões gerenciadas, um fixação de ponteiro é declarado qualificando uma declaração de ponteiro com o __pin palavra-chave. Aqui está um exemplo ligeiramente modificado em relação a especificação de gerenciado Extensions:
__gc struct H { int j; };
int main()
{
H * h = new H;
int __pin * k = & h -> j;
// …
};
No novo design de linguagem, um ponteiro pinning é declarado com sintaxe semelhante a que um ponteiro interior.
ref struct H
{
public:
int j;
};
int main()
{
H^ h = gcnew H;
pin_ptr<int> k = &h->j;
// …
}
Um ponteiro pinning sob a nova sintaxe é um caso especial de um ponteiro interior.As restrições originais de um ponteiro pinning permanecem.Por exemplo, ele não pode ser usado sistema autônomo um parâmetro ou tipo de um método de retorno; podem ser declarado apenas em um objeto de diretiva de grupo local.Um número de restrições adicionais, no entanto, foram adicionado na nova sintaxe.
O valor padrão de um ponteiro pinning é nullptr, não 0. A pin_ptr<> não é inicializado ou atribuído 0. Todas as atribuições de 0 em código existente precisará ser alterado para nullptr.
Um ponteiro pinning em gerenciado Extensions obteve permissão para um objeto inteiro, sistema autônomo no exemplo a seguir, extraído da especificação de gerenciado Extensions de endereço:
__gc class G {
public:
void incr(int* pi) { pi += 1; }
};
__gc struct H { int j; };
void f( G * g ) {
H __pin * pH = new H;
g->incr(& pH -> j);
};
Na sintaxe de novo, fixação de todo o objeto retornado pelo new Não há suporte para a expressão. Em vez disso, o endereço do membro interior precisa ser fixado.Por exemplo,
ref class G {
public:
void incr(int* pi) { *pi += 1; }
};
ref struct H { int j; };
void f( G^ g ) {
H ^ph = gcnew H;
Console::WriteLine(ph->j);
pin_ptr<int> pj = &ph->j;
g->incr( pj );
Console::WriteLine(ph->j);
}
Tipos de valor e seus comportamentos