Partilhar via


Notação de conversão e introdução de safe_cast<>

A notação convertido foi alterada de extensões gerenciadas para C++ a Visual C++.

Modificar uma estrutura existente é uma experiência diferente e mais difícil do que crafting a estrutura inicial. Há menos graus de versões, e a solução tende para um contrato entre a reestruturação ideal e o que é possível nas dependências estruturais existentes.

A extensão de idioma é um exemplo. Voltar ao início dos 90 como em orientando programação tornou-se um paradigma importante, a necessidade de uma facilidade fortemente tipado de entrada for AR em C++ ficou pressionando. Downcasting é a conversão explícita do usuário de um ponteiro ou uma referência a um ponteiro ou de uma referência de classe base de uma classe derivada. Downcasting requer uma conversão explícita. A razão é que o tipo real do ponteiro da classe base é um aspecto de tempo de execução; o compilador não pode verificar em virtude disso. Ou, para reformular isso, uma facilitar a entrada de aérea, como uma chamada de função virtual, requer algum formulário de resolução dinâmico. Isso gerencie duas perguntas:

  • Por que deve ser uma entrada for AR ser necessária em paradigmas orientado por objeto? Não é o mecanismo virtual da função suficiente? Ou seja, pois não é um reivindicar que toda a necessidade de uma entrada for AR (ou uma conversão de qualquer tipo) é uma falha de design?

  • Por que deve suporte de uma entrada for AR ser um problema em C++? Independentemente de tudo, não é um problema em idiomas orientados a objeto como tagarelice (ou, subsequentemente, o Java e o C#)? Que é sobre C++ que faz com suporte de uma facilidade de entrada for AR difícil?

Uma função virtual representa uma comum do algoritmo dependentes de classificação a uma família de tipos. (Não nós estamos considerando as interfaces, que não têm suporte em ISO-C++ mas são disponíveis em CLR que programas e que representam uma alternativa interessante de design). O design da família normalmente é representado por uma hierarquia da classe na qual há uma classe base abstrata que declara a interface comum (funções virtuais) e um conjunto de classes derivadas concretas que representam a família real no domínio de aplicativo.

Uma hierarquia de Light em um domínio de aplicativo (CGI) de aparência gerada por computador, por exemplo, terá atributos comuns como color, intensity, position, on, off, e assim por diante. Um pode controlar várias lights, usando a interface comum sem preocupação se uma leve específico é um projetor, uma pãozinho light direcionais, uma não direcional (pense de sol), ou talvez uma leve de celeiro- porta. Nesse caso, downcasting a luz- um tipo específico para exercer a interface virtual é desnecessário. Em um ambiente de produção, no entanto, a velocidade é essencial. Um pode entrada for AR e para invocar explicitamente cada método se ao fazer a execução de chamadas embutido pode ser executada em vez de usar o mecanismo virtual.

Assim, um motivo para a entrada for AR em C++ é suprimir o mecanismo virtual em troca de ganho significativo no desempenho de tempo de execução. (Observe que a automação dessa otimização manual é uma área de pesquisa ativa. Porém, é mais difícil resolver de substituir o uso explícito da palavra-chave de register ou de inline .)

Uma segunda motivo para a entrada for AR estiver fora da natureza dupla da polimorfismo. Uma maneira de display de polimorfismo está sendo particionada em um par passivo e dinâmico de formulários.

Uma invocação virtual (e a facilidade de entrada for AR) representam usa dinâmicas de polimorfismo: se estiver executando uma ação com base no tipo real do ponteiro da classe base naquela instância específica na execução do programa.

Atribuir um objeto de classe derivada ao ponteiro da classe base, o entanto, é uma forma de passivo polimorfismo; estiver usando polimorfismo como um mecanismo de transporte. Essa é o principal uso de Object, por exemplo, a programação predefinida genérico de CLR. Quando usado passiva, o ponteiro da classe base escolhido para o transporte e o armazenamento normalmente oferece uma interface que é muito abstrato. Object, por exemplo, fornece aproximadamente cinco métodos pela interface; nenhuma outra comportamento específico requer uma entrada for AR explícita. Por exemplo, se quisermos para ajustar o ângulo de nosso projetor ou sua taxa de queda, temos a entrada for AR explicitamente. Uma interface virtual dentro de uma família de tipos praticàvel secundária não pode ser um superconjunto de todos os métodos possíveis dos vários filhos, e assim que uma facilidade de entrada for AR será necessária sempre em uma linguagem orientado por objeto.

Se uma facilidade segura de entrada for AR é necessária em um idioma orientado por objeto, então por que tomou C++ tão por muito tempo para adicionar um? O problema está em como fazer informações sobre o tipo de tempo de execução do ponteiro disponível. No caso de uma função virtual, informações de tempo de execução é configurada em duas partes pelo compilador:

  • O objeto da classe contém um membro virtual adicional do ponteiro da tabela (o início ou término da classe do objeto; isso ocorre tem um histórico interessante propriamente dita) que trata da tabela virtual apropriado. Por exemplo, um objeto de projetor aborda a um projetor a tabela virtual, uma leve direcionais, uma tabela virtual clara direcionais, e assim por diante

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

O problema, em seguida, é como fazer as informações disponíveis do tipo para o ponteiro sem alterar o tamanho de ponteiros C++, ou adicionando um segundo endereço ou digite diretamente adicionando qualquer meio a codificação. Isso não for aceitável 2 os programadores (e) para programas que decidir usar o paradigmas orientado por objeto – que ainda era a comunidade de usuários frequente. Outra possibilidade foi introduzir um ponteiro especial para tipos polimorfos da classe, mas essa seria confusa, e torna difícil combinar os dois, especialmente com problemas de aritmética do ponteiro. Também não for aceitável manter uma tabela de tempo de execução que associasse cada ponteiro com seu tipo atualmente associado, e atualizar dinamicamente.

O problema é em um par de comunidades do usuário que têm diferentes aspirações mas legítimas de programação. A solução precisa ser um contrato entre as duas comunidades, permitindo a cada um não apenas a aspiração mas a capacidade interoperar. Isso significa que as soluções são oferecidas por um ou outro lado provavelmente serão inexequíveis e a solução implementada por fim para ser menor que perfeitas. A resolução real revolve o redor da definição de uma classe polimórfica: uma classe é polimórfica uma que contém uma função virtual. Uma classe polimórfica oferece suporte a uma entrada for AR fortemente tipado dinâmico. Isto resolve o problema de manter -- ponteiro-como- endereço como todas as classes polimorfos contiverem esse membro adicional do ponteiro a sua tabela virtual associado. As informações de tipo associado, consequentemente, pode ser armazenadas em uma estrutura virtual expandida da tabela. O custo de entrada for AR fortemente tipado (quase) são localizados os usuários da facilidade.

O problema com a seguinte entrada for AR fortemente tipado era a sintaxe. Como é uma conversão, a proposta original para o comitê de ISO-C++ usou a sintaxe convertido unadorned, como neste exemplo:

spot = ( SpotLight* ) plight;

mas isso foi descartado pelo comitê porque não permite que o usuário controla o custo de conversão. Se a entrada for AR fortemente tipado dinâmico tem a mesma sintaxe que anteriormente não seguro mas notação convertida estático, ele se tornará uma substituição, e o usuário não tem capacidade de suprimir a sobrecarga de tempo de execução quando é desnecessária e talvez muito dispendiosa.

Em geral, em C++, sempre há um mecanismo pelo qual para suprimir a funcionalidade completo suporte. Por exemplo, podemos desativar o mecanismo virtual usando o operador do escopo da classeBox::rotate(angle)() ou chamando o método virtual por meio de um objeto de classe (em vez de um ponteiro ou pela referência de aquela classe.) Esta última exclusão não é exigida pelo idioma mas é uma qualidade do problema de implementação, semelhante à exclusão de compilaçã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 );

Isso com proposta foi feito backup para uma consideração adicional, e as várias notações de backup foram consideradas, e essa trazida de volta ao comitê era de formato (?type), que indica seu indefinido - ou seja, natureza dinâmica. Isso atribuiu ao usuário a capacidade de ativar /desativar entre os dois formulários – estáticos ou dinâmicos – mas nenhuma foi muito satisfeito com eles. Isso era de volta à mesa de projeto. A terceira e notação com êxito agora é o dynamic_cast<type>padrão, que foi generalizado para um conjunto de quatro convertidos em notações de estilo.

Em ISO-C++, dynamic_cast retorna 0 quando aplicado a um tipo impróprio do ponteiro, e lançará uma exceção de std::bad_cast quando aplicado a um tipo de referência. Nas extensões gerenciadas para C++, aplicando dynamic_cast a um tipo gerenciado de referência (devido à sua representação do ponteiro) retornou sempre 0. __try_cast<type> foi apresentado como um analógico para a variante de emissão da exceção de dynamic_cast, exceto que gerencie 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 nova sintaxe, __try_cast foi reconvertida como safe_cast. Aqui está 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 o código verificável limitando a capacidade de desenvolvedores à conversão entre tipos nas maneiras que deixam de código não controlável. Este é um aspecto importante do paradigmas dinâmico de programação representado pela nova sintaxe. Por isso, as instâncias de conversões antiquadas foi reconvertida internamente como o tempo de execução converter, de modo que, por exemplo:

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

Por outro lado, pois polimorfismo fornece um modo ativo e passivo, algumas vezes é necessário executar apenas uma entrada for AR para obter acesso a API não virtual de um subtipo. Isso pode ocorrer, por exemplo, com o membro de uma classe que desejam identificar qualquer tipo dentro da hierarquia (polimorfismo passivo como um mecanismo de transporte) mas para os quais a instância atual em um contexto específico do programa é conhecido. Nesse caso, ter uma verificação de tempo de execução de conversão pode ser uma sobrecarga inaceitável. Se a nova sintaxe é servir como a linguagem de programação gerenciada dos sistemas, deve fornecer alguns mídia de permitir uma entrada for AR de tempo de compilação (ou seja, estático). O aplicativo de notação de static_cast é permitido é por isso que uma entrada for AR de tempo de compilação:

// 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á como garantir que o programador que faz static_cast está correto e bem-intencionado; isto é, não há como forçar o código gerenciado para estar verificável. Esta for uma preocupação mais urgente no paradigmas dinâmico de programa do que no modo nativo, mas não é suficiente em uma linguagem de programação do sistema para não permitir o usuário a capacidade de ativar /desativar entre uma estática e uma conversão de tempo de execução.

Há uma armadilha e uma armadilha de desempenho na nova sintaxe, porém. Em programação nativo, não há nenhuma diferença de desempenho entre a notação convertido desatualizada e a notação de static_cast do com estilo. Mas a nova sintaxe, a notação convertido desatualizada é significativamente mais caro que o uso de notação de static_cast do com estilo. A razão é que o compilador internamente transforma o uso de notação desatualizada em uma verificação de tempo de execução que gerou uma exceção. Além disso, também altera o perfil da execução do código porque causa uma exceção não identificada que leva abaixo do aplicativo – talvez sàbiamente, mas o mesmo erro não provocará essa exceção se a notação de static_cast foi usada. Se pode discutir isso ajudará usuários de pancada no usando a notação de estilo em. Mas só quando falhar; caso contrário, fará com que os programas que usam notação desatualizada para executar significativamente mais lento sem uma compreensão de como visível, semelhante às seguintes armadilhas do programador em linguagem 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)