Compartilhar via


Converter notação e introdução de < safe_cast >

A notação de conversão foi alterado de Managed Extensions for C++ para Visual C++.

Modificar uma estrutura existente é uma experiência diferente e mais difícil do que criar a estrutura inicial.Há menos graus de liberdade e a solução tende em direção a um compromisso entre uma reestruturação ideal e o que é praticável de acordo com as dependências estruturais existentes.

Extensão de linguagem é outro exemplo.No início dos anos 90 como programação orientar o objeto se tornou uma paradigma importante, a necessidade de um recurso de tipo seguro precise em C++ se tornou urgentes.Baixar é a conversão explícita do usuário de um ponteiro de classe base ou uma referência a um ponteiro ou referência de uma classe derivada.Baixar requer uma conversão explícita.O motivo é que o tipo real do ponteiro de classe base é um aspecto do tempo de execução; o compilador, portanto, não é possível verificá-lo.Ou, especifique que novamente, um recurso de lançamento decrescente, assim como uma chamada de função virtual requer alguma forma de resolução dinâmica.Isso gera duas perguntas:

  • Por que deve um lançamento decrescente ser necessário no paradigma orientado a objeto?Não é o mecanismo de função virtual suficiente?Isto é, por que não é uma declaração que qualquer necessidade de um lançamento decrescente (ou uma conversão de qualquer tipo) é uma falha de design?

  • Por que o suporte de um lançamento decrescente deve ser um problema no C++?Afinal, não é um problema no linguagens orientadas a objeto como Smalltalk (ou, posteriormente, Java e C#)?O que é sobre C++ que suporte um lançamento decrescente recurso difícil?

Uma função virtual representa um algoritmo de tipo dependente comuns a uma família de tipos.(Estamos não considerando interfaces, que não são suportadas no ISO C++ mas estão disponíveis na programação de CLR e que representam uma alternativa interessante de design).O design da família que geralmente é representado por uma hierarquia de classe na qual é uma classe base abstrata declarando a interface comum (funções virtuais) e um conjunto de classes derivadas concretas que representam os tipos de família reais no domínio do aplicativo.

A Light hierarquia em um domínio de aplicativo fotográficos gerado computador (CGI), por exemplo, ter atributos comuns, como color, intensity, position, on, offe assim por diante.Um pode controlar várias luzes, usando a interface comum sem se preocupar se uma determinada luz é um destaque, uma luz direcional, uma luz não-direcional (Imagine o sol) ou talvez uma luz de porta do celeiro.Nesse caso, baixar para um tipo específico de luz para exercitar sua interface virtual é desnecessário.No entanto, em um ambiente de produção, velocidade é essencial.Um talvez precise e explicitamente chamar cada método se, fazendo isso, a execução in-line das chamadas pode ser executada em vez de usar o mecanismo virtual.

Portanto, um motivo precise em C++ é para suprimir o mecanismo virtual por um ganho significativo no desempenho de tempo de execução.(Observe que a automação dessa otimização manual é uma área ativa de pesquisa.No entanto, é mais difícil de resolver que substituir o uso explícito do register ou inline palavra-chave.)

Uma segunda razão precise fica fora da natureza dupla do polimorfismo.Uma maneira de pensar em polimorfismo está sendo dividida em um par de passivo e dinâmico de formulários.

Uma chamada virtual (e um lançamento decrescente facility) representam dinâmicos usos polimorfismo: um está realizando uma ação com base no tipo real do ponteiro de classe base em determinada instância em execução do programa.

No entanto, atribuir um objeto de classe derivada para seu ponteiro de classe base, é uma forma passiva de polimorfismo; ele está usando o polimorfismo como um mecanismo de transporte.Este é o principal uso do Object, por exemplo, na pre-generic programação CLR.Quando usado passivamente, o ponteiro de classe base escolhido para transporte e armazenamento normalmente oferece uma interface que é muito abstrata.Object, por exemplo, fornece aproximadamente cinco métodos através de sua interface; qualquer comportamento mais específico requer um explícita lançamento decrescente.Por exemplo, se queremos ajustar o ângulo da nossa destaque ou sua taxa de outono off, teríamos que precise explicitamente.Uma interface virtual dentro de uma família de subtipos practicably não pode ser um superconjunto de todos os métodos possíveis de seus muitos filhos, e assim um recurso precise sempre serão necessários dentro de uma linguagem orientada a objeto.

Se um cofre precise facility é necessária em uma linguagem orientada a objeto, e por que ela levou C++ tão longa para adicionar um?O problema está em como disponibilizar as informações como o tipo de tempo de execução do ponteiro.No caso de uma função virtual, as informações de tempo de execução é configurar em duas partes pelo compilador:

  • O objeto de classe contém um membro de ponteiro adicional tabela virtual (seja no início ou fim do objeto de classe; que tem uma história interessante em si) que aborda a tabela virtual apropriada.Por exemplo, um objeto de destaque aborda uma tabela virtual destaque, uma luz direcional, direcional tabela virtual clara e assim por diante

  • Cada função virtual tem um associado fixo slot na tabela e a instância real para invocar é representada pelo endereço armazenado dentro da tabela.Por exemplo, o virtual Light destruidor pode estar associado a slot 0, Color com o slot 1 e assim por diante.Isso é uma estratégia eficiente se inflexível porque ele está configurado em tempo de compilação e representa uma sobrecarga mínima.

O problema é como disponibilizar as informações do tipo do ponteiro sem alterar o tamanho dos ponteiros do C++, adicionando um segundo endereço ou adicionando algum tipo de codificação tipo diretamente.Isso não seria aceitável para os programadores (e programas) que decidir não utilizar o paradigma orientado a objeto – ainda era a comunidade de usuários predominante.Outra possibilidade era apresentar um ponteiro especial para tipos de classe polimórfico, mas isso seria ser confuso e dificultar a inter-mix os dois, principalmente com problemas de aritmética de ponteiro.Também não seria aceitável para manter uma tabela de tempo de execução que associa cada ponteiro seu tipo atualmente associado e atualizá-lo dinamicamente.

O problema é então um par de comunidades de usuários que possuem diferentes mas legítimas aspirations de programação.A solução deve ser um compromisso entre as duas comunidades, permitindo que cada não apenas seu aspiration mas a capacidade de interoperar.Isso significa que as soluções oferecidas por ambos os lados devem ser inviável e a solução implementada finalmente ser menor do que perfeito.A resolução real gira em torno da definição de uma classe polimórfica: uma classe polimórfica é aquele que contém uma função virtual.Uma classe polimórfica oferece suporte a um cofre tipo dinâmico lançamento decrescente.Isso resolve o problema de manter-o--como-endereço de ponteiro porque todas as classes polimórficas contém esse membro de ponteiro adicional para sua tabela virtual associada.As informações de tipo associado, portanto, podem ser armazenadas em uma estrutura expandida tabela virtual.O custo do tipo-safe precise é (quase) localizado para usuários do recurso.

A próxima edição com o tipo seguro precise era sua sintaxe.Porque é uma projeção, a proposta original para o comitê do ISO C++ usado sintaxe cast acrescidos, como no exemplo:

spot = ( SpotLight* ) plight;

mas isso foi rejeitado pelo Comitê de porque ele não permitiu que o usuário controle o custo do tom.Se o seguro tipo dinâmico precise tem a mesma sintaxe como anteriormente não seguro, mas estático notação cast, então se torna uma substituição e o usuário tem a capacidade de suprimir a sobrecarga de runtime quando é desnecessário e talvez muito caro.

Em geral, em C++, sempre há um mecanismo pelo qual suprimir a funcionalidade de suporte de compilador.Por exemplo, podemos pode desativar o mecanismo virtual usando o operador de escopo de classe (Box::rotate(angle)) ou chamando o método virtual por meio de um objeto de classe (em vez de um ponteiro ou referência de classe).Este último supressão não é necessária a linguagem mas é uma qualidade de problema de implementação, semelhante a supressão da construção de um temporário em uma declaração do formulário:

// compilers are free to optimize away the temporary
X x = X::X( 10 );

Para que a proposta foi levada novamente para fazer mais considerações e vários notações alternativas foram consideradas e foi aquele trouxe de volta para o comitê do formulário (?type), que indicou indeterminado – ou seja, a natureza dinâmica.Isso fornecia ao usuário a capacidade de alternar entre dois formulários – estáticos ou dinâmicos – mas ninguém estava muito satisfeito com ele.Portanto, era para a placa de desenho.A notação de terceira e bem-sucedida é agora o padrão dynamic_cast<type>, que foi generalizada para um conjunto de quatro notações de conversão do novo estilo.

ISO C++ dynamic_cast retorna 0 quando aplicado a um tipo de ponteiro inadequado e lança um std::bad_cast exceção quando aplicado a um tipo de referência.Em Managed Extensions for C++, aplicando dynamic_cast para um tipo de referência gerenciado (por causa de sua representação de ponteiro) sempre retornado 0.__try_cast<type>foi introduzido como um análogo à exceção lançando variante de dynamic_cast, exceto que ele lança System::InvalidCastException se a conversão falhar.

public __gc class ItemVerb;
public __gc class ItemVerbCollection {
public:
   ItemVerb *EnsureVerbArray() [] {
      return __try_cast<ItemVerb *[]>
         (verbList->ToArray(__typeof(ItemVerb *)));
   }
};

Na sintaxe de novo, __try_cast foi recast como safe_cast.Aqui é o mesmo fragmento de código na nova sintaxe:

public ref class ItemVerb;
public ref class ItemVerbCollection {
public:
   array<ItemVerb^>^ EnsureVerbArray() {
      return safe_cast<array<ItemVerb^>^>
         ( verbList->ToArray( ItemVerb::typeid ));
   }
};

No mundo gerenciado, é importante permitir código verificável, limitando a capacidade de programadores converter entre tipos de maneiras que deixar o código não verificado.Este é um aspecto crítico do paradigma de programação dinâmico representado pela nova sintaxe.Por esse motivo, instâncias de conversões de estilo antigo são recast internamente como conversões de tempo de execução, portanto, que, por exemplo:

// internally recast into the 
// equivalent safe_cast expression above
( array<ItemVerb^>^ ) verbList->ToArray( ItemVerb::typeid ); 

Por outro lado, como polimorfismo fornece um ativo e um modo passivo, às vezes, é necessário executar um lançamento decrescente apenas para acessar a API não virtual de um subtipo.Isso pode ocorrer, por exemplo, com os membros de uma classe que deseja endereço qualquer tipo dentro da hierarquia (passivo polimorfismo como um mecanismo de transporte), mas para o qual a instância real em um contexto de determinado programa é conhecida.Nesse caso, ter uma verificação de tempo de execução do tom pode ser uma sobrecarga inaceitável.Se a nova sintaxe servir como os linguagem de programação de sistemas gerenciados, forneça alguns meios de permitir um tempo de compilação (isto é, estáticas) precise.É por isso que a aplicação de static_cast notação pode permanecer um tempo de compilação precise:

// ok: cast performed at compile-time. 
// No run-time check for type correctness
static_cast< array<ItemVerb^>^>(verbList->ToArray(ItemVerb::typeid));

O problema é que não há nenhuma maneira para garantir que o programador fazendo o static_cast está correto e bem-intencionados; ou seja, não é possível forçar o código gerenciado seja verificável.Isso é uma preocupação mais urgente sob o paradigma de programa dinâmico que em nativo, mas não é suficiente dentro de um sistema de programação idioma para impedir que o usuário a capacidade de alternar entre um estático e projeção de tempo de execução.

Há uma armadilha de desempenho e armadilha na nova sintaxe, no entanto.Na programação nativa, não há nenhuma diferença no desempenho entre a notação de conversão de estilo antigo e o novo estilo static_cast notação.Mas a nova sintaxe, a notação de estilo antigo cast é significativamente mais cara que o uso do novo estilo static_cast notação.O motivo é que o compilador transforma internamente o uso da notação de estilo antigo em uma verificação de tempo de execução lança uma exceção.Além disso, ele também altera o perfil de execução do código porque ele faz com que uma exceção não capturada trazendo o aplicativo – talvez com sabedoria, mas o mesmo erro não causaria essa exceção se o static_cast notação foram usados.Alguém poderia argumentar que isso ajudará Prod usuários em usando a notação de estilo novo.Mas somente quando ele falhar; Caso contrário, ele fará com que programas que usam a notação de estilo antigo para executar significativamente mais lentos sem uma compreensão visível do motivo, semelhantes às seguintes armadilhas programador de C:

// pitfall # 1: 
// initialization can remove a temporary class object, 
// assignment cannot
Matrix m;
m = another_matrix;

// pitfall # 2: declaration of class objects far from their use
Matrix m( 2000, 2000 ), n( 2000, 2000 );
if ( ! mumble ) return;

Consulte também

Referência

Conversões de Estilo C-Style com /clr (C++/CLI)

safe_cast (Extensões de Componentes C++)

Conceitos

Alterações gerais em linguagens (C++/CLI)