Ouvi dizer que a static_cast
função deve ser preferida ao estilo C ou simples ao estilo de função. Isso é verdade? Por quê?
Ouvi dizer que a static_cast
função deve ser preferida ao estilo C ou simples ao estilo de função. Isso é verdade? Por quê?
Respostas:
A principal razão é que moldes clássicos C não fazem distinção entre o que chamamos static_cast<>()
, reinterpret_cast<>()
, const_cast<>()
, e dynamic_cast<>()
. Essas quatro coisas são completamente diferentes.
A static_cast<>()
é geralmente seguro. Há uma conversão válida no idioma ou um construtor apropriado que o torna possível. A única vez que é um pouco arriscado é quando você baixa para uma classe herdada; você deve se certificar de que o objeto é realmente o descendente que você afirma ser, por meios externos ao idioma (como uma bandeira no objeto). A dynamic_cast<>()
é seguro desde que o resultado seja verificado (ponteiro) ou uma possível exceção seja levada em consideração (referência).
A reinterpret_cast<>()
(ou a const_cast<>()
), por outro lado, é sempre perigoso. Você diz ao compilador: "confie em mim: eu sei que isso não se parece com um foo
(parece que não é mutável), mas é".
O primeiro problema é que é quase impossível dizer qual deles ocorrerá em um elenco no estilo C, sem examinar pedaços de código grandes e dispersos e conhecer todas as regras.
Vamos assumir o seguinte:
class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;
CMyBase *pSomething; // filled somewhere
Agora, esses dois são compilados da mesma maneira:
CDerivedClass *pMyObject;
pMyObject = static_cast<CDerivedClass*>(pSomething); // Safe; as long as we checked
pMyObject = (CDerivedClass*)(pSomething); // Same as static_cast<>
// Safe; as long as we checked
// but harder to read
No entanto, vamos ver esse código quase idêntico:
CMyOtherStuff *pOther;
pOther = static_cast<CMyOtherStuff*>(pSomething); // Compiler error: Can't convert
pOther = (CMyOtherStuff*)(pSomething); // No compiler error.
// Same as reinterpret_cast<>
// and it's wrong!!!
Como você pode ver, não há uma maneira fácil de distinguir entre as duas situações sem saber muito sobre todas as classes envolvidas.
O segundo problema é que as projeções no estilo C são muito difíceis de localizar. Em expressões complexas, pode ser muito difícil visualizar projeções no estilo C. É praticamente impossível escrever uma ferramenta automatizada que precise localizar conversões no estilo C (por exemplo, uma ferramenta de pesquisa) sem um front-end completo do compilador C ++. Por outro lado, é fácil procurar por "static_cast <" ou "reinterpret_cast <".
pOther = reinterpret_cast<CMyOtherStuff*>(pSomething);
// No compiler error.
// but the presence of a reinterpret_cast<> is
// like a Siren with Red Flashing Lights in your code.
// The mere typing of it should cause you to feel VERY uncomfortable.
Isso significa que, não apenas os lançamentos no estilo C são mais perigosos, mas é muito mais difícil encontrá-los para garantir que eles estejam corretos.
static_cast
para derrubar uma hierarquia de herança, mas sim dynamic_cast
. Isso retornará o ponteiro nulo ou um ponteiro válido.
static_cast
nessa situação. dynamic_cast
pode ser mais seguro, mas nem sempre é a melhor opção. Às vezes, você sabe que um ponteiro aponta para um determinado subtipo, por meio opaco ao compilador, e a static_cast
é mais rápido. Em pelo menos alguns ambientes, dynamic_cast
requer suporte opcional do compilador e custo de tempo de execução (habilitando o RTTI), e talvez você não queira habilitá-lo apenas para algumas verificações, você mesmo pode fazer. O RTTI do C ++ é apenas uma solução possível para o problema.
static_cast
. O equivalente C de reinterpret_cast
é *(destination_type *)&
, ou seja, pegar o endereço do objeto, converter esse endereço em um ponteiro para um tipo diferente e, em seguida, desreferenciar. Excepto no caso de tipos de caracteres ou de certos tipos de struct para o qual C define o comportamento dessa construção, que geralmente resulta em comportamento indefinido em C.
int
(e int
sozinho), por que usar static_cast<int>
vs. (int)
como o único benefício parece ser com variáveis de classe e ponteiros. Solicite que você elabore sobre isso.
int
dynamic_cast
não se aplica, mas todas as outras razões permanecem. Por exemplo: digamos que v
seja um parâmetro de função declarado como float
, então (int)v
é static_cast<int>(v)
. Mas se você alterar o parâmetro para float*
, (int)v
em silêncio torna-se reinterpret_cast<int>(v)
enquanto static_cast<int>(v)
é ilegal e corretamente pego pelo compilador.
Uma dica pragmática: você pode pesquisar facilmente a palavra-chave static_cast no seu código-fonte se planeja arrumar o projeto.
int
parâmetro.
Em resumo :
static_cast<>()
dá a você a capacidade de verificar o tempo de compilação, o elenco do estilo C não.static_cast<>()
pode ser visto facilmente em qualquer lugar dentro de um código fonte C ++; em contraste, o elenco de C_Style é mais difícil de detectar.- As intenções são transmitidas muito melhor usando conversões de C ++.
Mais explicações :
A conversão estática realiza conversões entre tipos compatíveis . É semelhante ao elenco do estilo C, mas é mais restritivo. Por exemplo, a conversão no estilo C permitiria que um ponteiro inteiro aponte para um caractere.
char c = 10; // 1 byte int *p = (int*)&c; // 4 bytes
Como isso resulta em um ponteiro de 4 bytes apontando para 1 byte de memória alocada, a gravação nesse ponteiro causará um erro em tempo de execução ou substituirá parte da memória adjacente.
*p = 5; // run-time error: stack corruption
Ao contrário da conversão em estilo C, a conversão estática permitirá que o compilador verifique se os tipos de dados de ponteiro e ponta são compatíveis, o que permite ao programador capturar essa atribuição incorreta de ponteiro durante a compilação.
int *q = static_cast<int*>(&c); // compile-time error
Leia mais em:
Qual é a diferença entre static_cast <> e conversão de estilo C
e conversão
regular vs. static_cast vs. dynamic_cast
static_cast<>()
é mais legível. Quero dizer, às vezes é, mas na maioria das vezes - especialmente em tipos inteiros básicos - é horrível e desnecessariamente detalhado. Por exemplo: Esta é uma função que troca os bytes de uma palavra de 32 bits. Seria quase impossível ler usando static_cast<uint##>()
moldes, mas é bastante fácil de entender usando (uint##)
moldes. Imagem do código: imgur.com/NoHbGve
always
. (mas na maioria das vezes sim) Há casos em que o estilo c é muito mais legível. Essa é uma das razões pelas quais a conversão de estilo c ainda está ativa e ativa no im ++ do c ++. :) A propósito, esse foi um exemplo muito bom
(uint32_t)(uint8_t)
) para atingir os bytes além dos mais baixos. Para isso há bit a bit e ( 0xFF &
). O uso de elencos está ofuscando a intenção.
A questão é maior do que apenas usar a conversão wither static_cast ou estilo C, porque há coisas diferentes que acontecem ao usar moldes no estilo C. Os operadores de conversão de C ++ destinam-se a tornar essas operações mais explícitas.
Na superfície, static_cast e estilo C lançam a mesma coisa, por exemplo, ao converter um valor para outro:
int i;
double d = (double)i; //C-style cast
double d2 = static_cast<double>( i ); //C++ cast
Ambos convertem o valor inteiro em um dobro. No entanto, ao trabalhar com ponteiros, as coisas ficam mais complicadas. alguns exemplos:
class A {};
class B : public A {};
A* a = new B;
B* b = (B*)a; //(1) what is this supposed to do?
char* c = (char*)new int( 5 ); //(2) that weird?
char* c1 = static_cast<char*>( new int( 5 ) ); //(3) compile time error
Neste exemplo (1) talvez esteja OK porque o objeto apontado por A é realmente uma instância de B. Mas e se você não souber nesse ponto do código o que realmente aponta? (2) talvez perfeitamente legal (você só quer olhar para um byte do número inteiro), mas também pode ser um erro; nesse caso, um erro seria bom, como (3). Os operadores de conversão de C ++ destinam-se a expor esses problemas no código, fornecendo erros em tempo de compilação ou tempo de execução, quando possível.
Portanto, para estrita "conversão de valor", você pode usar static_cast. Se você desejar a conversão polimórfica de ponteiros em tempo de execução, use dynamic_cast. Se você realmente deseja esquecer os tipos, pode usar reintrepret_cast. E para jogar const pela janela, const_cast.
Eles apenas tornam o código mais explícito, para que você saiba o que estava fazendo.
static_cast
significa que você não pode acidentalmente const_cast
ou reinterpret_cast
, o que é uma coisa boa.
Consulte Introdução eficaz ao C ++
É sobre quanta segurança de tipo você deseja impor.
Quando você escreve (bar) foo
(o que equivale a reinterpret_cast<bar> foo
se você não forneceu um operador de conversão de tipos), está dizendo ao compilador para ignorar a segurança de tipos e faça o que é solicitado.
Ao escrever, static_cast<bar> foo
você solicita ao compilador que verifique pelo menos se a conversão de tipo faz sentido e, para tipos integrais, insira algum código de conversão.
EDIT 2014-02-26
Escrevi esta resposta há mais de 5 anos e entendi errado. (Ver comentários.) Mas ainda recebe votos!
static_cast<bar>(foo)
, entre parênteses. O mesmo para reinterpret_cast<bar>(foo)
.
As conversões de estilo C são fáceis de perder em um bloco de código. As conversões no estilo C ++ não são apenas melhores práticas; eles oferecem um grau muito maior de flexibilidade.
reinterpret_cast permite integrais para conversões de tipo de ponteiro, no entanto, pode ser inseguro se mal utilizado.
static_cast oferece uma boa conversão para tipos numéricos, por exemplo, de enums para ints ou ints para floats ou quaisquer tipos de dados dos quais você confia. Ele não executa nenhuma verificação de tempo de execução.
O dynamic_cast, por outro lado, executará essas verificações sinalizando quaisquer atribuições ou conversões ambíguas. Funciona apenas em ponteiros e referências e gera uma sobrecarga.
Existem alguns outros, mas estes são os principais que você encontrará.
static_cast, além de manipular ponteiros para classes, também pode ser usado para realizar conversões explicitamente definidas em classes, bem como para executar conversões padrão entre tipos fundamentais:
double d = 3.14159265;
int i = static_cast<int>(d);
static_cast<int>(d)
, porém, quando (int)d
é tão mais conciso e legível? (Quero dizer, no caso dos tipos básicos, e não ponteiros de objeto.)
(int)d
quando int{d}
é muito mais legível? A ()
sintaxe do construtor ou da função, se você tiver , não é tão rápida para se transformar em um labirinto de pesadelos de parênteses em expressões complexas. Nesse caso, seria em int i{d}
vez de int i = (int)d
. Muito melhor IMO. Dito isto, quando eu só preciso de uma expressão temporária, eu uso static_cast
e nunca usei moldes de construtores, não acho. Eu só uso (C)casts
quando apressadamente escrito depuração cout
s ...