Notação de projeção e introdução de safe_cast <>
A notação de projeção mudou de gerenciado Extensions para C++ para Visual C++ 2008.
Modificar uma estrutura existente é uma experiência diferente e mais difícil do que criar a estrutura inicial.Há menos graus de liberdade e tende a solução em direção a um compromisso entre uma reestruturação ideal e o que é practicable dadas as dependências estruturais existentes.
Extensão de linguagem é outro exemplo.No início dos anos 90 sistema autônomo programação orientar o objeto se tornou uma paradigma importante, a necessidade de um recurso de fortemente tipado downcast 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 downcast, assim como uma telefonar de função virtual requer alguns formulário de resolução dinâmica.Isso gera duas perguntas:
Por que deve um downcast ser necessário no paradigma orientada a objeto?Não é o mecanismo de função virtual suficiente?Ou seja, por que não é possível uma declaração que qualquer precisa para um downcast (ou uma conversão de qualquer classificar) é uma falha de design?
Por que o suporte de um downcast deve ser um problema no C++?Afinal, não é um problema em linguagens orientadas a objeto, sistema autônomo Smalltalk (ou, em seguida, Java e translation from VPE for Csharp)?O que é sobre C++ que faz com que um recurso downcast difícil de suporte?
Uma função virtual representa um algoritmo de dependentes do tipo comum a uma família de tipos.(Estamos não considerando interfaces, que não são suportadas no ISO-C++, mas estão disponível em programação CLR e que representam uma alternativa interessante de design).O design da família que geralmente é representado por uma hierarquia de classe no qual há uma classe base abstrata declarando a interface comum (funções virtual) e um conjunto de classes concretas derivadas que representam os tipos real da família no domínio do aplicativo.
A Light hierarquia em um domínio do aplicativo do computador gerado por imagem (CGI), por exemplo, tem atributos comuns, sistema autônomo color, intensity, position, on, off, e assim por diante. Um pode controlar várias luzes, usando a interface comum sem se preocupar se uma luz específica é uma luz de spot, uma luz direcional, uma luz direcional não (Imagine o SOL) ou talvez uma luz celeiro-door.Nesse caso, baixar a um tipo específico de luz para exercitar seu interface virtual é desnecessário.No entanto, em um ambiente de produção, velocidade é essencial.Um talvez downcast e chamar explicitamente cada método se, ao fazê-lo, execução in-line das chamadas pode ser executada em vez de usar o mecanismo virtual.
Assim, um motivo para downcast em C++ é para suprimir o mecanismo virtual troca para um ganho significativo no desempenho em tempo de execução.(Observe que a automação dessa otimização manual é uma área ativo de pesquisa.No entanto, é mais difícil de resolver de substituí o uso explícito do register ou inline palavra-chave.)
Um segundo motivo para downcast fica fora da natureza dupla do polimorfismo.Uma forma de polimorfismo está sendo dividida em um emparelhar de passivo e dinâmico de formulários.
Uma chamada virtual (e um recurso downcast) representam dinâmicos usos de polimorfismo: um está realizando uma ação com base no tipo real do ponteiro classe base por essa instância em particular na execução do programa.
Atribuindo um objeto de classe derivada para sua classe base ponteiro, entretanto, é uma forma passiva de polimorfismo; ele está usando o polimorfismo sistema autônomo um mecanismo de transporte.Este é o principal uso do Object, por exemplo, em programação CLR pre-generic. Quando usado passivamente, o ponteiro de classe base escolhido para transporte e armazenamento normalmente oferece uma interface que é muito abstrata.Object, para exemplo, fornece aproximadamente cinco métodos por meio de sua interface; qualquer comportamento mais específico requer um explícita downcast. Por exemplo, se quisermos ajustar o ângulo da nossa destaque ou sua taxa de outono logoff, teríamos para downcast 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 filhos muitos e portanto, um recurso downcast sempre serão necessários dentro de uma linguagem orientada a objeto.
Se um cofre downcast recurso é necessário em uma linguagem orientada a objeto e, em seguida, por demora C++ tanto tempo para adicionar um?O problema está em sistema autônomo tornar sistema autônomo informações sistema autônomo o em time de execução tipo do ponteiro disponível.Em caso de uma função virtual, as informações em time de execução são conjunto cima em duas partes pelo compilador:
O objeto de classe contém um membro de ponteiro adicional tabela virtual (no início ou participante do objeto de classe; ’s que tem uma história interessante em si) que aborda a tabela virtual apropriada.Por exemplo, um objeto de destaque atende a uma tabela virtual destaque, uma luz direcional, um 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 Destrutor 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 no momento da compilar e representa uma sobrecarga mínima.
O problema, em seguida, é como disponibilizar o tipo de informação do ponteiro sem alterar o dimensionar dos ponteiros de C++, adicionando um segundo endereço ou adicionando diretamente algum tipo de codificação tipo.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 dois, particularmente com problemas de aritmética de ponteiro.Também não é aceitável para manter um em time de execução tabela associa cada ponteiro com o tipo de associado no momento e atualizá-lo dinamicamente.
O problema, em seguida, é um emparelhar de comunidades de usuários que possuem diferentes mas legítimas aspirations programação.A solução deve ser um compromisso entre as duas comunidades, permitindo que cada não somente sua 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 que perfeito.A resolução real envolve a definição de uma classe polimórfica: uma classe polimórfica é aquele que contém uma função virtual.Uma classe polimórfica suporta um dinâmico fortemente tipado downcast.Este procedimento resolve o problema maintain-a-ponteiro-sistema autônomo-endereço porque todas sistema autônomo 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 de tabela virtual expandido.O custo de fortemente tipado downcast (quase) está localizado a usuários do recurso.
O próximo problema com a segurança de tipos downcast foi sua sintaxe.Porque é uma projeção, a proposta ao Comitê de ISO-C++ original utilizou a sintaxe do elenco unadorned, sistema autônomo no exemplo:
spot = ( SpotLight* ) plight;
mas isso foi rejeitado pelo comitê porque ele não permitiu que o usuário controle o custo da conversão.Se o dinâmico fortemente tipado downcast tem a mesma sintaxe que anteriormente não seguro, mas estático notação de projeção e, em seguida, ele se torna uma substituição e o usuário tem sem capacidade para suprimir a sobrecarga de tempo de execução quando for desnecessário e talvez muito cara.
Em geral, em C++, sempre há um mecanismo pelo qual suprimir compatíveis com o compilador de funcionalidade.Por exemplo, 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 uma referência da classe). Esse último supressão não é necessária para o linguagem, mas é uma qualidade de problema de implementação, semelhante a supressão de 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 );
Assim, a proposta foi tirada volta para consideração adicional e várias alternativas notações foram consideradas e aquele trazidas de volta ao Comitê de era do formulário (?type), que indicado indeterminado – ou seja, a natureza dinâmica. Isso deu o usuário a capacidade de alternar entre as duas formas – estático ou dinâmico – mas ninguém estava muito satisfeito com ele.Portanto, era para a placa de desenho.A notação de terceira e bem-sucedida é o padrão agora dynamic_cast<type>, que foi generalizada para um conjunto de quatro notações de conversão do novo estilo.
Em ISO-C++, dynamic_cast Retorna 0 Quando aplicada a um tipo de ponteiro inadequado e lança um std::bad_cast exceção quando aplicado a um tipo de referência. Nas extensões gerenciadas para C++, aplicando dynamic_cast a um tipo de referência gerenciado (por causa de sua representação de ponteiro) sempre retornado 0. __try_cast<type> foi apresentado sistema autônomo um analógico à exceção lançando variante a 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 tem sido recast sistema autônomo safe_cast. Eis o fragmento de código mesmo a 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 para converter entre tipos de forma a deixar o código não verificado.Isso é um aspecto crucial 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 sistema autônomo em time de execução projeta, isso que, por exemplo:
// internally recast into the
// equivalent safe_cast expression above
( array<ItemVerb^>^ ) verbList->ToArray( ItemVerb::typeid );
Por Outros lado, como o polimorfismo oferece um ativo e um modo passivo, às vezes, é necessário executar downcast apenas para acessar a API não-virtual de um subtipo.Isso pode ocorrer, por exemplo, com sistema autônomo membros de uma classe que deseja endereço qualquer tipo dentro da hierarquia (passivo polimorfismo sistema autônomo um mecanismo de transporte), mas para a qual a instância real em um contexto de programa específico é conhecida.Nesse caso, tendo um em time de execução verificação da conversão pode ser uma sobrecarga inaceitável.Se a nova sintaxe deve servir sistema autônomo sistema autônomo linguagem de programação de sistemas gerenciado, ele deverá fornecer alguns meios de permitir um time de compilar (isto é, estáticas) downcast.É por isso que a aplicação do static_cast notação pode permanecer um time de compilar downcast:
// 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 well-intentioned; ou seja, não é possível forçar o código gerenciado seja verificável. Isso é uma preocupação mais urgente sob o paradigma dinâmico do programa que em nativo, mas não é suficiente dentro de um sistema a capacidade de alternar entre um estático e projeção de time de execução de linguagem para impedir que o usuário de programação.
Há uma interceptação de desempenho e armadilha na nova sintaxe entretanto.Em nativo programação, não há nenhuma diferença no desempenho entre a notação de projeção de estilo antigo e o novo estilo static_cast notação. Mas a nova sintaxe, a notação de estilo antigo conversão é 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 em time de execução que lança uma exceção.Além disso, ele também altera o analisar de execução do código porque ele faz com que uma exceção não tratada, trazendo o aplicativo – talvez com sabedoria, mas o mesmo erro não faria com que essa exceção se o static_cast notação foram usados. Alguém poderia argumentar que isso ajudará a prod usuários em usando a notação de estilo novo.Mas somente quando ele falhar; caso contrário, ele farão com que programas que usam a notação de estilo antigo para execução significativamente mais lenta sem um conhecimento 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
Conceitos
Referência
comCcomacomscomtcomscom comCcom-comEcomscomtcomicomlcomocom comwcomicomtcomhcom com/comCcomLcomRcom