Para alguns exemplos concretos úteis do uso de diferentes tipos de elencos, você pode verificar a primeira resposta em uma pergunta semelhante neste outro tópico .
22417 TeaMonkie
2
Você pode encontrar respostas realmente boas para sua pergunta acima. Mas eu gostaria de colocar mais um ponto aqui, @ e.James "Não há nada que esses novos operadores de conversão de c ++ possam fazer e que a conversão de estilo c não possa. Eles são adicionados mais ou menos para melhorar a legibilidade do código."
BreakBadSP
@BreakBadSP Os novos lançamentos não são apenas para uma melhor legibilidade do código. Eles estão lá para tornar mais difícil fazer coisas perigosas, como rejeitar const ou lançar ponteiros em vez de seus valores. static_cast tem muito menos possibilidades de fazer algo perigoso do que um elenco de estilo ac!
FourtyTwo
@FourtyTwo concordou
BreakBadSP
Respostas:
2571
static_casté o primeiro elenco que você deve tentar usar. Ele faz coisas como conversões implícitas entre tipos (como intpara floatou ponteiro para void*) e também pode chamar funções de conversão explícitas (ou implícitas). Em muitos casos, declarar explicitamente static_castnão é necessário, mas é importante observar que a T(something)sintaxe é equivalente (T)somethinge deve ser evitada (mais sobre isso mais tarde). A T(something, something_else)é seguro, no entanto, e garantido para chamar o construtor.
static_casttambém pode transmitir através de hierarquias de herança. É desnecessário ao converter para cima (em direção a uma classe base), mas ao converter para baixo, ele pode ser usado desde que não seja transmitido por virtualherança. Entretanto, ele não verifica, e é um comportamento indefinido static_castdiminuir uma hierarquia para um tipo que não é realmente o tipo do objeto.
const_castpode ser usado para remover ou adicionar consta uma variável; nenhum outro elenco C ++ é capaz de removê-lo (nem mesmo reinterpret_cast). É importante observar que a modificação de um constvalor anterior é indefinida apenas se a variável original for const; se você usá-lo para retirar constuma referência a algo que não foi declarado const, é seguro. Isso pode ser útil ao sobrecarregar funções-membro com base em const, por exemplo. Também pode ser usado para adicionar consta um objeto, como chamar uma sobrecarga de função de membro.
const_casttambém funciona de maneira semelhante volatile, embora isso seja menos comum.
dynamic_casté usado exclusivamente para lidar com polimorfismo. Você pode converter um ponteiro ou referência a qualquer tipo polimórfico para qualquer outro tipo de classe (um tipo polimórfico possui pelo menos uma função virtual, declarada ou herdada). Você pode usá-lo para mais do que apenas lançar para baixo - você pode lançar para o lado ou até outra cadeia. O dynamic_castprocurará o objeto desejado e o retornará, se possível. Se não puder, retornará nullptrno caso de um ponteiro ou lançará std::bad_castno caso de uma referência.
dynamic_casttem algumas limitações, no entanto. Não funciona se houver vários objetos do mesmo tipo na hierarquia de herança (o chamado 'diamante temido') e você não estiver usando virtualherança. Ele também só pode passar por herança pública - sempre falhará em atravessar protectedou privateherança. Isso raramente é um problema, no entanto, como essas formas de herança são raras.
reinterpret_casté o elenco mais perigoso e deve ser usado com moderação. Ele transforma um tipo diretamente em outro - como converter o valor de um ponteiro para outro, ou armazenar um ponteiro em um intou em todos os tipos de outras coisas desagradáveis. Em geral, a única garantia que você obtém reinterpret_casté que, normalmente, se você converter o resultado de volta ao tipo original, obterá exatamente o mesmo valor (mas não se o tipo intermediário for menor que o tipo original). Há várias conversões que reinterpret_casttambém não podem ser realizadas. É usado principalmente para conversões e manipulações de bits particularmente estranhas, como transformar um fluxo de dados brutos em dados reais ou armazenar dados nos bits mais baixos de um ponteiro para dados alinhados.
Molde-estilo C e fundido de estilo função são moldes usando (type)objectou type(object), respectivamente, e são funcionalmente equivalentes. Eles são definidos como o primeiro dos seguintes itens bem-sucedidos:
const_cast
static_cast (embora ignorando as restrições de acesso)
static_cast (veja acima), então const_cast
reinterpret_cast
reinterpret_cast, então const_cast
Portanto, ele pode ser usado como um substituto para outros elencos em alguns casos, mas pode ser extremamente perigoso devido à capacidade de se transformar em um reinterpret_cast, e o último deve ser preferido quando a conversão explícita for necessária, a menos que você tenha certeza de que static_castterá êxito ou reinterpret_castfalhará . Mesmo assim, considere a opção mais longa e explícita.
As transmissões no estilo C também ignoram o controle de acesso ao executar a static_cast, o que significa que elas têm a capacidade de executar uma operação que nenhuma outra transmissão pode. No entanto, isso é principalmente uma confusão e, na minha opinião, é apenas mais um motivo para evitar elencos no estilo C.
dynamic_cast é apenas para tipos polimórficos. você só precisa usá-lo quando estiver transmitindo para uma classe derivada. static_cast é certamente a primeira opção, a menos que você precise especificamente da funcionalidade de dynamic_cast. Não é um elenco miraculoso de bala de prata em geral.
jalf
2
Ótima resposta! Uma observação rápida: static_cast pode ser necessário para converter a hierarquia no caso de você ter um Derivado * & para converter em Base * &, pois ponteiros / referências duplos não lançam automaticamente a hierarquia. Me deparei com essa situação (francamente, não comum) dois minutos atrás. ;-)
bartgol
5
* "nenhum outro elenco de C ++ é capaz de remover const(nem mesmo reinterpret_cast)" ... sério? Que tal reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))?
precisa saber é o seguinte
29
Eu acho que um detalhe importante que faltava acima é que o dynamic_cast tem uma penalidade de desempenho em tempo de execução em comparação com o static ou reinterpret_cast. Isso é importante, por exemplo, em software em tempo real.
Jritz42
5
Pode valer a pena mencionar que reinterpret_castmuitas vezes é a arma de escolha quando se trata de jogo de uma API de tipos de dados opacos
Classe Skeleton
333
Use dynamic_castpara converter ponteiros / referências dentro de uma hierarquia de herança.
Use static_castpara conversões de tipo comuns.
Use reinterpret_castpara reinterpretação de baixo nível de padrões de bits. Use com extrema cautela.
Use const_castpara jogar fora const/volatile. Evite isso, a menos que você esteja preso usando uma API const-incorreta.
Cuidado com dynamic_cast. Ele se baseia no RTTI e isso não funcionará conforme o esperado nos limites das bibliotecas compartilhadas. Simplesmente porque você cria uma biblioteca executável e compartilhada de forma independente, não há uma maneira padronizada de sincronizar o RTTI entre diferentes compilações. Por esse motivo, na biblioteca Qt, existe qobject_cast <> que usa as informações do tipo QObject para verificar os tipos.
user3150128
198
(Muita explicação teórica e conceitual foi dada acima)
Abaixo estão alguns exemplos práticos quando usei static_cast , dynamic_cast , const_cast , reinterpret_cast .
OnEventData(void* pData){......// pData is a void* pData, // EventData is a structure e.g. // typedef struct _EventData {// std::string id;// std:: string remote_id;// } EventData;// On Some Situation a void pointer *pData// has been static_casted as // EventData* pointer EventData*evtdata =static_cast<EventData*>(pData);.....}
A teoria de algumas das outras respostas é boa, mas ainda confusa, ver esses exemplos depois de ler as outras respostas faz com que todas façam sentido. Isso é sem os exemplos, eu ainda não tinha certeza, mas com eles, agora tenho certeza sobre o que as outras respostas significam.
Solx
1
Sobre o último uso de reinterpret_cast: não é o mesmo que usar static_cast<char*>(&val)?
Lorenzo Belli 27/05
3
@LorenzoBelli Claro que não. Você tentou? O último não é C ++ válido e bloqueia a compilação. static_castsó funciona entre tipos com conversões definidas, relação visível por herança ou para / de void *. Para todo o resto, existem outros elencos. É permitido reinterpret casta qualquer char *tipo permitir a leitura da representação de qualquer objeto - e um dos únicos casos em que essa palavra-chave é útil, e não um gerador desenfreado de implementação / comportamento indefinido. Mas isso não é considerado uma conversão 'normal', portanto não é permitido pelo (geralmente) muito conservador static_cast.
underscore_d
2
reinterpret_cast é bastante comum quando você está trabalhando com software do sistema, como bancos de dados. Na maioria dos casos, você escreve seu próprio gerenciador de páginas, que não tem idéia sobre qual é o tipo de dados armazenado na página e retorna apenas um ponteiro nulo. Cabe aos níveis mais altos fazer uma reinterpretação do elenco e inferir como quiser.
Sohaib 17/05
1
O exemplo const_cast exibe comportamento indefinido. Uma variável declarada como const não pode ser desconstituída. No entanto, uma variável declarada como não-const que é passada para uma função que faz referência a const pode ser deformada nessa função sem que seja UB.
9788 Johann Gerell
99
Pode ajudar se você conhecer um pouco de internos ...
static_cast
O compilador C ++ já sabe como converter entre tipos de scaler, como float para int. Use static_castpara eles.
Quando você pede ao compilador para converter do tipo Apara B, o construtor de static_castchamadas Bpassa Acomo param. Como alternativa, Apoderia ter um operador de conversão (ou seja A::operator B()). Se Bnão tiver esse construtor ou Anão tiver um operador de conversão, você receberá um erro de tempo de compilação.
A conversão de A*para B*sempre é bem-sucedida se A e B estão na hierarquia de herança (ou nula), caso contrário, você obtém um erro de compilação.
Entendi : Se você converter o ponteiro base para o ponteiro derivado, mas se o objeto real não for realmente um tipo derivado, não haverá erro. Você fica com um ponteiro ruim e muito provavelmente um segfault em tempo de execução. O mesmo vale para A&a B&.
Gotcha : Transmitir de Derived to Base ou vice-versa cria uma nova cópia! Para pessoas provenientes de C # / Java, isso pode ser uma grande surpresa, porque o resultado é basicamente um objeto cortado, criado a partir do Derived.
dynamic_cast
dynamic_cast usa informações de tipo de tempo de execução para descobrir se a conversão é válida. Por exemplo, (Base*)a (Derived*)pode falhar se apontador não é, na verdade, do tipo derivado.
Isso significa que dynamic_cast é muito caro comparado ao static_cast!
Para A*a B*, se elenco é dynamic_cast então inválida retornará nullptr.
Para A&a B&se fundido é inválido, em seguida, dynamic_cast vai jogar exceção bad_cast.
Diferente de outras transmissões, há sobrecarga de tempo de execução.
const_cast
Enquanto static_cast pode fazer non-const para const, não pode ir ao contrário. O const_cast pode fazer nos dois sentidos.
Um exemplo em que isso é útil é a iteração em algum contêiner, como o set<T>que apenas retorna seus elementos como const para garantir que você não altere sua chave. No entanto, se sua intenção é modificar os membros não-chave do objeto, tudo ficará bem. Você pode usar const_cast para remover constness.
Outro exemplo é quando você deseja implementar T& SomeClass::foo()também const T& SomeClass::foo() const. Para evitar duplicação de código, você pode aplicar const_cast para retornar o valor de uma função de outra.
reinterpret_cast
Isso basicamente diz que pegue esses bytes nesse local de memória e pense nele como um objeto específico.
Por exemplo, você pode carregar 4 bytes de float a 4 bytes de int para ver como são os bits no float.
Obviamente, se os dados não estiverem corretos para o tipo, você poderá obter segfault.
Não há sobrecarga de tempo de execução para este elenco.
Eu adicionei as informações do operador de conversão, mas há algumas outras coisas que devem ser corrigidas também e não me sinto tão confortável atualizando isso demais. Os itens são: 1. If you cast base pointer to derived pointer but if actual object is not really derived type then you don't get error. You get bad pointer and segfault at runtime.Você recebe UB, o que pode resultar em um segfault em tempo de execução, se você tiver sorte. 2. Modelos dinâmicos também podem ser usados em cross casting. 3. O elenco Const pode resultar em UB em alguns casos. Usar mutablepode ser uma opção melhor para implementar a constância lógica.
Adrian
1
@ Adrian, você está correto em todos os aspectos. A resposta é escrito para pessoas mais ou menos nível iniciante e eu não queria sobrecarregá-los com todas as outras complicações que vem com mutable, fundição cruz etc.
Eu nunca usei reinterpret_caste me pergunto se encontrar um caso que precise dele não é um cheiro de design ruim. Na base de código em que trabalho dynamic_casté muito usada. A diferença static_casté que a dynamic_castverificação em tempo de execução pode ser (mais segura) ou não (mais sobrecarga) o que você deseja (consulte msdn ).
Eu usei reintrepret_cast para uma finalidade - obter os bits de um dobro (mesmo tamanho desde que na minha plataforma).
Joshua
2
reinterpret_cast é necessário, por exemplo, para trabalhar com objetos COM. CoCreateInstance () possui um parâmetro de saída do tipo void ** (o último parâmetro), no qual você passará o ponteiro declarado como, por exemplo, "INetFwPolicy2 * pNetFwPolicy2". Para fazer isso, você precisa escrever algo como reinterpret_cast <void **> (& pNetFwPolicy2).
Serge Rogatch
1
Talvez exista uma abordagem diferente, mas eu uso reinterpret_castpara extrair pedaços de dados de uma matriz. Por exemplo, se eu tenho um char*contendo um grande buffer cheio de dados binários compactados que eu preciso percorrer e obter primitivas individuais de tipos variados. Algo parecido com isto: #template<class ValType> unsigned int readValFromAddress(char* addr, ValType& val) { /*On platforms other than x86(_64) this could do unaligned reads, which could be bad*/ val = (*(reinterpret_cast<ValType*>(addr))); return sizeof(ValType); }
James Matta
Eu nunca usei reinterpret_cast, não há muitos usos para isso.
Pika, o Mago das Baleias,
Pessoalmente, eu só vi reinterpret_castusado por uma razão. Vi dados brutos do objeto armazenados em um tipo de dados "blob" em um banco de dados e, quando os dados são recuperados do banco de dados, reinterpret_castsão usados para transformar esses dados brutos no objeto.
ImaginHuman072889
15
Além das outras respostas até agora, aqui está um exemplo não óbvio, onde static_castnão é suficiente para que reinterpret_castseja necessário. Suponha que exista uma função que em um parâmetro de saída retorne ponteiros para objetos de diferentes classes (que não compartilham uma classe base comum). Um exemplo real dessa função é CoCreateInstance()(consulte o último parâmetro, que é de fato void**). Suponha que você solicite determinada classe de objeto dessa função, para que você conheça com antecedência o tipo do ponteiro (o que costuma fazer para objetos COM). Nesse caso, você não pode converter o ponteiro para o ponteiro void**com static_cast: você precisa reinterpret_cast<void**>(&yourPointer).
Em código:
#include<windows.h>#include<netfw.h>.....INetFwPolicy2* pNetFwPolicy2 =nullptr;
HRESULT hr =CoCreateInstance(__uuidof(NetFwPolicy2),nullptr,
CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),//static_cast<void**>(&pNetFwPolicy2) would give a compile errorreinterpret_cast<void**>(&pNetFwPolicy2));
No entanto, static_castfunciona para ponteiros simples (não ponteiros para ponteiros), portanto, o código acima pode ser reescrito para evitar reinterpret_cast(ao preço de uma variável extra) da seguinte maneira:
Não funcionaria algo como em &static_cast<void*>(pNetFwPolicy2)vez de static_cast<void**>(&pNetFwPolicy2)?
precisa saber é
9
Enquanto outras respostas descrevem bem todas as diferenças entre os lançamentos em C ++, gostaria de acrescentar uma breve nota sobre por que você não deve usar os lançamentos em estilo C (Type) vare Type(var).
Para iniciantes em C ++, as transmissões no estilo C parecem ser a operação de superconjunto nas transmissões em C ++ (static_cast <> (), dynamic_cast <> (), const_cast <> (), reinterpret_cast <> ()) e alguém poderia preferir elas nas transmissões em C ++ . De fato, o elenco no estilo C é o superconjunto e mais curto para escrever.
O principal problema dos elencos no estilo C é que eles ocultam a real intenção do desenvolvedor. As conversões no estilo C podem executar praticamente todos os tipos de conversão, desde conversões normalmente seguras feitas por static_cast <> () e dynamic_cast <> () até conversões potencialmente perigosas como const_cast <> (), onde o modificador const pode ser removido para que as variáveis const pode ser modificado e reinterpret_cast <> () que pode até reinterpretar valores inteiros em ponteiros.
Aqui está a amostra.
int a=rand();// Random number.int* pa1=reinterpret_cast<int*>(a);// OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.int* pa2=static_cast<int*>(a);// Compiler error.int* pa3=dynamic_cast<int*>(a);// Compiler error.int* pa4=(int*) a;// OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.*pa4=5;// Program crashes.
A principal razão pela qual os lançamentos de C ++ foram adicionados à linguagem foi permitir que um desenvolvedor esclarecesse suas intenções - por que ele fará esse elenco. Ao usar conversões no estilo C, perfeitamente válidas em C ++, você torna seu código menos legível e mais suscetível a erros, especialmente para outros desenvolvedores que não criaram seu código. Portanto, para tornar seu código mais legível e explícito, você sempre deve preferir conversões em C ++ em vez de conversões em estilo C.
Aqui está uma pequena citação do livro de Bjarne Stroustrup (o autor do C ++) The C ++ Programming Language 4th edition - page 302.
Essa conversão no estilo C é muito mais perigosa do que os operadores de conversão nomeados, porque a notação é mais difícil de detectar em um programa grande e o tipo de conversão pretendida pelo programador não é explícito.
Somente a linha (4) é compilada sem erros. Somente reinterpret_cast pode ser usado para converter um ponteiro em um objeto em um ponteiro em qualquer tipo de objeto não relacionado.
Um exemplo a ser observado é: O dynamic_cast falharia em tempo de execução; no entanto, na maioria dos compiladores, também falha na compilação porque não há funções virtuais na estrutura do ponteiro sendo convertido, o que significa que o dynamic_cast funcionará apenas com ponteiros de classe polimórficos .
Quando usar o elenco do C ++ :
Use static_cast como o equivalente a uma conversão no estilo C que valoriza a conversão, ou quando precisamos converter explicitamente um ponteiro de uma classe para sua superclasse.
Use const_cast para remover o qualificador const.
Use reinterpret_cast para fazer conversões inseguras de tipos de ponteiros de e para números inteiros e outros tipos de ponteiros. Use isso apenas se soubermos o que estamos fazendo e entendermos os problemas de alias.
static_castvs dynamic_castvs reinterpret_castinterna vista em um downcast / upcast
Nesta resposta, quero comparar esses três mecanismos em um exemplo concreto de upcast / downcast e analisar o que acontece com os ponteiros / memória / assembly subjacentes para fornecer uma compreensão concreta de como eles se comparam.
Acredito que isso dará uma boa intuição sobre como esses elencos são diferentes:
static_cast: faz um deslocamento de endereço no tempo de execução (baixo impacto no tempo de execução) e nenhuma verificação de segurança de que um downcast está correto.
dyanamic_cast: faz o mesmo endereço compensado no tempo de execução static_cast, mas também e uma verificação de segurança dispendiosa de que um downcast está correto usando RTTI.
Essa verificação de segurança permite consultar se um ponteiro de classe base é de um determinado tipo em tempo de execução, verificando um retorno nullptrque indica um downcast inválido.
Portanto, se o seu código não conseguir verificar isso nullptre executar uma ação válida sem interrupção, você deve apenas usar em static_castvez da conversão dinâmica.
Se uma interrupção é a única ação que seu código pode executar, talvez você queira apenas ativar as dynamic_castcompilações in debug (-NDEBUG ) e usar o static_castcontrário, por exemplo, como feito aqui , para não desacelerar suas execuções rápidas.
reinterpret_cast: não faz nada em tempo de execução, nem mesmo o deslocamento do endereço. O ponteiro deve apontar exatamente para o tipo correto, nem mesmo uma classe base funciona. Você geralmente não deseja isso, a menos que fluxos de bytes brutos estejam envolvidos.
Considere o seguinte exemplo de código:
main.cpp
#include<iostream>struct B1 {
B1(int int_in_b1): int_in_b1(int_in_b1){}virtual~B1(){}void f0(){}virtualint f1(){return1;}int int_in_b1;};struct B2 {
B2(int int_in_b2): int_in_b2(int_in_b2){}virtual~B2(){}virtualint f2(){return2;}int int_in_b2;};struct D :public B1,public B2 {
D(int int_in_b1,int int_in_b2,int int_in_d): B1(int_in_b1), B2(int_in_b2), int_in_d(int_in_d){}void d(){}int f2(){return3;}int int_in_d;};int main(){
B2 *b2s[2];
B2 b2{11};
D *dp;
D d{1,2,3};// The memory layout must support the virtual method call use case.
b2s[0]=&b2;// An upcast is an implicit static_cast<>().
b2s[1]=&d;
std::cout <<"&d "<<&d << std::endl;
std::cout <<"b2s[0] "<< b2s[0]<< std::endl;
std::cout <<"b2s[1] "<< b2s[1]<< std::endl;
std::cout <<"b2s[0]->f2() "<< b2s[0]->f2()<< std::endl;
std::cout <<"b2s[1]->f2() "<< b2s[1]->f2()<< std::endl;// Now for some downcasts.// Cannot be done implicitly// error: invalid conversion from ‘B2*’ to ‘D*’ [-fpermissive]// dp = (b2s[0]);// Undefined behaviour to an unrelated memory address because this is a B2, not D.
dp =static_cast<D*>(b2s[0]);
std::cout <<"static_cast<D*>(b2s[0]) "<< dp << std::endl;
std::cout <<"static_cast<D*>(b2s[0])->int_in_d "<< dp->int_in_d << std::endl;// OK
dp =static_cast<D*>(b2s[1]);
std::cout <<"static_cast<D*>(b2s[1]) "<< dp << std::endl;
std::cout <<"static_cast<D*>(b2s[1])->int_in_d "<< dp->int_in_d << std::endl;// Segfault because dp is nullptr.
dp =dynamic_cast<D*>(b2s[0]);
std::cout <<"dynamic_cast<D*>(b2s[0]) "<< dp << std::endl;//std::cout << "dynamic_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;// OK
dp =dynamic_cast<D*>(b2s[1]);
std::cout <<"dynamic_cast<D*>(b2s[1]) "<< dp << std::endl;
std::cout <<"dynamic_cast<D*>(b2s[1])->int_in_d "<< dp->int_in_d << std::endl;// Undefined behaviour to an unrelated memory address because this// did not calculate the offset to get from B2* to D*.
dp =reinterpret_cast<D*>(b2s[1]);
std::cout <<"reinterpret_cast<D*>(b2s[1]) "<< dp << std::endl;
std::cout <<"reinterpret_cast<D*>(b2s[1])->int_in_d "<< dp->int_in_d << std::endl;}
Agora, como mencionado em: https://en.wikipedia.org/wiki/Virtual_method_table , a fim de oferecer suporte às chamadas de método virtual com eficiência, a estrutura de dados da memória Ddeve se parecer com:
B1:+0: pointer to virtual method table of B1
+4: value of int_in_b1
B2:+0: pointer to virtual method table of B2
+4: value of int_in_b2
D:+0: pointer to virtual method table of D (for B1)+4: value of int_in_b1
+8: pointer to virtual method table of D (for B2)+12: value of int_in_b2
+16: value of int_in_d
O fato principal é que a estrutura de dados da memória Dcontém dentro dela uma estrutura de memória compatível com a de B1e com a deB2 internamente.
Portanto, chegamos à conclusão crítica:
um upcast ou downcast precisa apenas mudar o valor do ponteiro por um valor conhecido em tempo de compilação
Dessa maneira, quando Dé passado para a matriz de tipos base, o tipo de conversão calcula esse deslocamento e aponta algo que se parece exatamente com um válido B2na memória:
b2s[1]=&d;
exceto que este possui a vtable em Dvez de B2e, portanto, todas as chamadas virtuais funcionam de forma transparente.
Agora, finalmente podemos voltar ao tipo de vazamento e à análise de nosso exemplo concreto.
A partir da saída stdout, vemos:
&d 0x7fffffffc930
b2s[1]0x7fffffffc940
Portanto, o implícito static_castfeito lá calculou corretamente o deslocamento da Destrutura de dados completa em 0x7fffffffc930 para a B2similar que está em 0x7fffffffffc940. Também inferimos que o que está entre 0x7fffffffc930 e 0x7fffffffcc40 é provavelmente os B1dados e a tabela.
Então, nas seções de baixa, agora é fácil entender como os inválidos falham e por que:
static_cast<D*>(b2s[0]) 0x7fffffffc910: o compilador subiu 0x10 nos bytes de tempo de compilação para tentar ir de um B2para o que continhaD
Mas porque b2s[0]não era umD , agora aponta para uma região de memória indefinida.
Primeiro, há uma verificação NULL e ela retorna NULL se a entrada for NULL.
Caso contrário, ele configura alguns argumentos no RDX, RSI e RDI e chama __dynamic_cast .
Não tenho paciência para analisar isso mais agora, mas, como outros disseram, a única maneira de isso funcionar é __dynamic_cast acessar algumas estruturas de dados RTTI na memória extras que representam a hierarquia de classes.
Portanto, ele deve começar a partir da B2entrada para essa tabela e, em seguida, percorrer essa hierarquia de classes até encontrar a tabela de tabela para uma Dconversão de tipob2s[0] .
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940este acredita em nós cegamente: dissemos que existe um Dendereço at b2s[1], e o compilador não faz cálculos de deslocamento.
Mas isso está errado, porque D está realmente em 0x7fffffffc930, o que está em 0x7fffffffcc40 é a estrutura do tipo B2 dentro de D! Portanto, o lixo é acessado.
Podemos confirmar isso na -O0montagem horrenda que apenas move o valor:
Respostas:
static_cast
é o primeiro elenco que você deve tentar usar. Ele faz coisas como conversões implícitas entre tipos (comoint
parafloat
ou ponteiro paravoid*
) e também pode chamar funções de conversão explícitas (ou implícitas). Em muitos casos, declarar explicitamentestatic_cast
não é necessário, mas é importante observar que aT(something)
sintaxe é equivalente(T)something
e deve ser evitada (mais sobre isso mais tarde). AT(something, something_else)
é seguro, no entanto, e garantido para chamar o construtor.static_cast
também pode transmitir através de hierarquias de herança. É desnecessário ao converter para cima (em direção a uma classe base), mas ao converter para baixo, ele pode ser usado desde que não seja transmitido porvirtual
herança. Entretanto, ele não verifica, e é um comportamento indefinidostatic_cast
diminuir uma hierarquia para um tipo que não é realmente o tipo do objeto.const_cast
pode ser usado para remover ou adicionarconst
a uma variável; nenhum outro elenco C ++ é capaz de removê-lo (nem mesmoreinterpret_cast
). É importante observar que a modificação de umconst
valor anterior é indefinida apenas se a variável original forconst
; se você usá-lo para retirarconst
uma referência a algo que não foi declaradoconst
, é seguro. Isso pode ser útil ao sobrecarregar funções-membro com base emconst
, por exemplo. Também pode ser usado para adicionarconst
a um objeto, como chamar uma sobrecarga de função de membro.const_cast
também funciona de maneira semelhantevolatile
, embora isso seja menos comum.dynamic_cast
é usado exclusivamente para lidar com polimorfismo. Você pode converter um ponteiro ou referência a qualquer tipo polimórfico para qualquer outro tipo de classe (um tipo polimórfico possui pelo menos uma função virtual, declarada ou herdada). Você pode usá-lo para mais do que apenas lançar para baixo - você pode lançar para o lado ou até outra cadeia. Odynamic_cast
procurará o objeto desejado e o retornará, se possível. Se não puder, retornaránullptr
no caso de um ponteiro ou lançarástd::bad_cast
no caso de uma referência.dynamic_cast
tem algumas limitações, no entanto. Não funciona se houver vários objetos do mesmo tipo na hierarquia de herança (o chamado 'diamante temido') e você não estiver usandovirtual
herança. Ele também só pode passar por herança pública - sempre falhará em atravessarprotected
ouprivate
herança. Isso raramente é um problema, no entanto, como essas formas de herança são raras.reinterpret_cast
é o elenco mais perigoso e deve ser usado com moderação. Ele transforma um tipo diretamente em outro - como converter o valor de um ponteiro para outro, ou armazenar um ponteiro em umint
ou em todos os tipos de outras coisas desagradáveis. Em geral, a única garantia que você obtémreinterpret_cast
é que, normalmente, se você converter o resultado de volta ao tipo original, obterá exatamente o mesmo valor (mas não se o tipo intermediário for menor que o tipo original). Há várias conversões quereinterpret_cast
também não podem ser realizadas. É usado principalmente para conversões e manipulações de bits particularmente estranhas, como transformar um fluxo de dados brutos em dados reais ou armazenar dados nos bits mais baixos de um ponteiro para dados alinhados.Molde-estilo C e fundido de estilo função são moldes usando
(type)object
outype(object)
, respectivamente, e são funcionalmente equivalentes. Eles são definidos como o primeiro dos seguintes itens bem-sucedidos:const_cast
static_cast
(embora ignorando as restrições de acesso)static_cast
(veja acima), entãoconst_cast
reinterpret_cast
reinterpret_cast
, entãoconst_cast
Portanto, ele pode ser usado como um substituto para outros elencos em alguns casos, mas pode ser extremamente perigoso devido à capacidade de se transformar em um
reinterpret_cast
, e o último deve ser preferido quando a conversão explícita for necessária, a menos que você tenha certeza de questatic_cast
terá êxito oureinterpret_cast
falhará . Mesmo assim, considere a opção mais longa e explícita.As transmissões no estilo C também ignoram o controle de acesso ao executar a
static_cast
, o que significa que elas têm a capacidade de executar uma operação que nenhuma outra transmissão pode. No entanto, isso é principalmente uma confusão e, na minha opinião, é apenas mais um motivo para evitar elencos no estilo C.fonte
const
(nem mesmoreinterpret_cast
)" ... sério? Que talreinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))
?reinterpret_cast
muitas vezes é a arma de escolha quando se trata de jogo de uma API de tipos de dados opacosUse
dynamic_cast
para converter ponteiros / referências dentro de uma hierarquia de herança.Use
static_cast
para conversões de tipo comuns.Use
reinterpret_cast
para reinterpretação de baixo nível de padrões de bits. Use com extrema cautela.Use
const_cast
para jogar foraconst/volatile
. Evite isso, a menos que você esteja preso usando uma API const-incorreta.fonte
(Muita explicação teórica e conceitual foi dada acima)
Abaixo estão alguns exemplos práticos quando usei static_cast , dynamic_cast , const_cast , reinterpret_cast .
(Também se refere a isso para entender a explicação: http://www.cplusplus.com/doc/tutorial/typecasting/ )
static_cast:
dynamic_cast:
const_cast:
reinterpret_cast:
fonte
static_cast<char*>(&val)
?static_cast
só funciona entre tipos com conversões definidas, relação visível por herança ou para / devoid *
. Para todo o resto, existem outros elencos. É permitidoreinterpret cast
a qualquerchar *
tipo permitir a leitura da representação de qualquer objeto - e um dos únicos casos em que essa palavra-chave é útil, e não um gerador desenfreado de implementação / comportamento indefinido. Mas isso não é considerado uma conversão 'normal', portanto não é permitido pelo (geralmente) muito conservadorstatic_cast
.Pode ajudar se você conhecer um pouco de internos ...
static_cast
static_cast
para eles.A
paraB
, o construtor destatic_cast
chamadasB
passaA
como param. Como alternativa,A
poderia ter um operador de conversão (ou sejaA::operator B()
). SeB
não tiver esse construtor ouA
não tiver um operador de conversão, você receberá um erro de tempo de compilação.A*
paraB*
sempre é bem-sucedida se A e B estão na hierarquia de herança (ou nula), caso contrário, você obtém um erro de compilação.A&
aB&
.dynamic_cast
(Base*)
a(Derived*)
pode falhar se apontador não é, na verdade, do tipo derivado.A*
aB*
, se elenco é dynamic_cast então inválida retornará nullptr.A&
aB&
se fundido é inválido, em seguida, dynamic_cast vai jogar exceção bad_cast.const_cast
set<T>
que apenas retorna seus elementos como const para garantir que você não altere sua chave. No entanto, se sua intenção é modificar os membros não-chave do objeto, tudo ficará bem. Você pode usar const_cast para remover constness.T& SomeClass::foo()
tambémconst T& SomeClass::foo() const
. Para evitar duplicação de código, você pode aplicar const_cast para retornar o valor de uma função de outra.reinterpret_cast
fonte
If you cast base pointer to derived pointer but if actual object is not really derived type then you don't get error. You get bad pointer and segfault at runtime.
Você recebe UB, o que pode resultar em um segfault em tempo de execução, se você tiver sorte. 2. Modelos dinâmicos também podem ser usados em cross casting. 3. O elenco Const pode resultar em UB em alguns casos. Usarmutable
pode ser uma opção melhor para implementar a constância lógica.mutable
, fundição cruz etc.Será que isso responde sua pergunta?
Eu nunca usei
reinterpret_cast
e me pergunto se encontrar um caso que precise dele não é um cheiro de design ruim. Na base de código em que trabalhodynamic_cast
é muito usada. A diferençastatic_cast
é que adynamic_cast
verificação em tempo de execução pode ser (mais segura) ou não (mais sobrecarga) o que você deseja (consulte msdn ).fonte
reinterpret_cast
para extrair pedaços de dados de uma matriz. Por exemplo, se eu tenho umchar*
contendo um grande buffer cheio de dados binários compactados que eu preciso percorrer e obter primitivas individuais de tipos variados. Algo parecido com isto: #template<class ValType> unsigned int readValFromAddress(char* addr, ValType& val) { /*On platforms other than x86(_64) this could do unaligned reads, which could be bad*/ val = (*(reinterpret_cast<ValType*>(addr))); return sizeof(ValType); }
reinterpret_cast
, não há muitos usos para isso.reinterpret_cast
usado por uma razão. Vi dados brutos do objeto armazenados em um tipo de dados "blob" em um banco de dados e, quando os dados são recuperados do banco de dados,reinterpret_cast
são usados para transformar esses dados brutos no objeto.Além das outras respostas até agora, aqui está um exemplo não óbvio, onde
static_cast
não é suficiente para quereinterpret_cast
seja necessário. Suponha que exista uma função que em um parâmetro de saída retorne ponteiros para objetos de diferentes classes (que não compartilham uma classe base comum). Um exemplo real dessa função éCoCreateInstance()
(consulte o último parâmetro, que é de fatovoid**
). Suponha que você solicite determinada classe de objeto dessa função, para que você conheça com antecedência o tipo do ponteiro (o que costuma fazer para objetos COM). Nesse caso, você não pode converter o ponteiro para o ponteirovoid**
comstatic_cast
: você precisareinterpret_cast<void**>(&yourPointer)
.Em código:
No entanto,
static_cast
funciona para ponteiros simples (não ponteiros para ponteiros), portanto, o código acima pode ser reescrito para evitarreinterpret_cast
(ao preço de uma variável extra) da seguinte maneira:fonte
&static_cast<void*>(pNetFwPolicy2)
vez destatic_cast<void**>(&pNetFwPolicy2)
?Enquanto outras respostas descrevem bem todas as diferenças entre os lançamentos em C ++, gostaria de acrescentar uma breve nota sobre por que você não deve usar os lançamentos em estilo C
(Type) var
eType(var)
.Para iniciantes em C ++, as transmissões no estilo C parecem ser a operação de superconjunto nas transmissões em C ++ (static_cast <> (), dynamic_cast <> (), const_cast <> (), reinterpret_cast <> ()) e alguém poderia preferir elas nas transmissões em C ++ . De fato, o elenco no estilo C é o superconjunto e mais curto para escrever.
O principal problema dos elencos no estilo C é que eles ocultam a real intenção do desenvolvedor. As conversões no estilo C podem executar praticamente todos os tipos de conversão, desde conversões normalmente seguras feitas por static_cast <> () e dynamic_cast <> () até conversões potencialmente perigosas como const_cast <> (), onde o modificador const pode ser removido para que as variáveis const pode ser modificado e reinterpret_cast <> () que pode até reinterpretar valores inteiros em ponteiros.
Aqui está a amostra.
A principal razão pela qual os lançamentos de C ++ foram adicionados à linguagem foi permitir que um desenvolvedor esclarecesse suas intenções - por que ele fará esse elenco. Ao usar conversões no estilo C, perfeitamente válidas em C ++, você torna seu código menos legível e mais suscetível a erros, especialmente para outros desenvolvedores que não criaram seu código. Portanto, para tornar seu código mais legível e explícito, você sempre deve preferir conversões em C ++ em vez de conversões em estilo C.
Aqui está uma pequena citação do livro de Bjarne Stroustrup (o autor do C ++) The C ++ Programming Language 4th edition - page 302.
fonte
Para entender, vamos considerar abaixo o trecho de código:
Somente a linha (4) é compilada sem erros. Somente reinterpret_cast pode ser usado para converter um ponteiro em um objeto em um ponteiro em qualquer tipo de objeto não relacionado.
Um exemplo a ser observado é: O dynamic_cast falharia em tempo de execução; no entanto, na maioria dos compiladores, também falha na compilação porque não há funções virtuais na estrutura do ponteiro sendo convertido, o que significa que o dynamic_cast funcionará apenas com ponteiros de classe polimórficos .
Quando usar o elenco do C ++ :
fonte
static_cast
vsdynamic_cast
vsreinterpret_cast
interna vista em um downcast / upcastNesta resposta, quero comparar esses três mecanismos em um exemplo concreto de upcast / downcast e analisar o que acontece com os ponteiros / memória / assembly subjacentes para fornecer uma compreensão concreta de como eles se comparam.
Acredito que isso dará uma boa intuição sobre como esses elencos são diferentes:
static_cast
: faz um deslocamento de endereço no tempo de execução (baixo impacto no tempo de execução) e nenhuma verificação de segurança de que um downcast está correto.dyanamic_cast
: faz o mesmo endereço compensado no tempo de execuçãostatic_cast
, mas também e uma verificação de segurança dispendiosa de que um downcast está correto usando RTTI.Essa verificação de segurança permite consultar se um ponteiro de classe base é de um determinado tipo em tempo de execução, verificando um retorno
nullptr
que indica um downcast inválido.Portanto, se o seu código não conseguir verificar isso
nullptr
e executar uma ação válida sem interrupção, você deve apenas usar emstatic_cast
vez da conversão dinâmica.Se uma interrupção é a única ação que seu código pode executar, talvez você queira apenas ativar as
dynamic_cast
compilações in debug (-NDEBUG
) e usar ostatic_cast
contrário, por exemplo, como feito aqui , para não desacelerar suas execuções rápidas.reinterpret_cast
: não faz nada em tempo de execução, nem mesmo o deslocamento do endereço. O ponteiro deve apontar exatamente para o tipo correto, nem mesmo uma classe base funciona. Você geralmente não deseja isso, a menos que fluxos de bytes brutos estejam envolvidos.Considere o seguinte exemplo de código:
main.cpp
Compile, execute e desmonte com:
onde
setarch
esta usado para desativar o ASLR para facilitar a comparação de execuções.Saída possível:
Agora, como mencionado em: https://en.wikipedia.org/wiki/Virtual_method_table , a fim de oferecer suporte às chamadas de método virtual com eficiência, a estrutura de dados da memória
D
deve se parecer com:O fato principal é que a estrutura de dados da memória
D
contém dentro dela uma estrutura de memória compatível com a deB1
e com a deB2
internamente.Portanto, chegamos à conclusão crítica:
Dessa maneira, quando
D
é passado para a matriz de tipos base, o tipo de conversão calcula esse deslocamento e aponta algo que se parece exatamente com um válidoB2
na memória:exceto que este possui a vtable em
D
vez deB2
e, portanto, todas as chamadas virtuais funcionam de forma transparente.Agora, finalmente podemos voltar ao tipo de vazamento e à análise de nosso exemplo concreto.
A partir da saída stdout, vemos:
Portanto, o implícito
static_cast
feito lá calculou corretamente o deslocamento daD
estrutura de dados completa em 0x7fffffffc930 para aB2
similar que está em 0x7fffffffffc940. Também inferimos que o que está entre 0x7fffffffc930 e 0x7fffffffcc40 é provavelmente osB1
dados e a tabela.Então, nas seções de baixa, agora é fácil entender como os inválidos falham e por que:
static_cast<D*>(b2s[0]) 0x7fffffffc910
: o compilador subiu 0x10 nos bytes de tempo de compilação para tentar ir de umB2
para o que continhaD
Mas porque
b2s[0]
não era umD
, agora aponta para uma região de memória indefinida.A desmontagem é:
então vemos que o GCC faz:
D
que não existedynamic_cast<D*>(b2s[0]) 0
: C ++ realmente descobriu que o elenco era inválido e retornadonullptr
!Não há como isso ser feito em tempo de compilação, e confirmaremos isso a partir da desmontagem:
Primeiro, há uma verificação NULL e ela retorna NULL se a entrada for NULL.
Caso contrário, ele configura alguns argumentos no RDX, RSI e RDI e chama
__dynamic_cast
.Não tenho paciência para analisar isso mais agora, mas, como outros disseram, a única maneira de isso funcionar é
__dynamic_cast
acessar algumas estruturas de dados RTTI na memória extras que representam a hierarquia de classes.Portanto, ele deve começar a partir da
B2
entrada para essa tabela e, em seguida, percorrer essa hierarquia de classes até encontrar a tabela de tabela para umaD
conversão de tipob2s[0]
.É por isso que reinterpretar o elenco é potencialmente caro! Aqui está um exemplo em que um patch de um liner que converte a
dynamic_cast
em umstatic_cast
em um projeto complexo reduziu o tempo de execução em 33%! .reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940
este acredita em nós cegamente: dissemos que existe umD
endereço atb2s[1]
, e o compilador não faz cálculos de deslocamento.Mas isso está errado, porque D está realmente em 0x7fffffffc930, o que está em 0x7fffffffcc40 é a estrutura do tipo B2 dentro de D! Portanto, o lixo é acessado.
Podemos confirmar isso na
-O0
montagem horrenda que apenas move o valor:Perguntas relacionadas:
Testado no Ubuntu 18.04 amd64, GCC 7.4.0.
fonte