Precisa de esclarecimentos no estilo de C, reinterpretar e const lança
-
23-09-2019 - |
Pergunta
Estou certo em assumir que C-estilo de lança (que são desencorajados) são nada, mas reinterpret_casts?Usando o último é visualmente impressionante e fácil de pesquisar quando se olha para o desagradável lança, e, portanto, recomenda-se sobre o C-estilo de lança?
Se lançando de si a const usando const_cast e a escrita originalmente para um objeto const não está definida, qual é o propósito de const_cast?
Nota: Eu sei que Bjarne, com razão, condena operações de fundição de que eles são seguros, e vai mesmo ao ponto de afirmar que "Uma feio a operação deve ter um feio sintática forma." e, consequentemente, o detalhamento de fundição de operadores em C++.Então, eu vou tentar minimizar o seu uso.Promessa.:)
Solução
Não.C o elenco pode fazer o equivalente a um const_cast
, um static_cast
, um reinterpret_cast
, ou uma combinação destes.No caso em que isso não era o bastante, ele pode também fazer pelo menos um pequeno truque que nenhum a combinação da mais recente da lança pode fazer em tudo!
Você pode usar const_cast
com resultados predefinidos se a variável original é definido sem const
, mas tudo que você tem é um const
ponteiro ou referência a esse objeto.OTOH, se você acha que você tem uma boa razão para usar um const_cast
, as chances são de que você deve realmente olhar para cima mutable
em vez disso.
Editar:Acho que eu deveria ter dito isso direito, mas um estilo de C elenco pode converter para um inacessíveis classe base.Por exemplo, considerar algo como:
[Editar:Estou atualizando o código para algo que vai compilar e (geralmente) demonstrar o problema.]
#include <iostream>
class base1 {
public:
virtual void print() { std::cout << "base 1\n"; }
};
class base2 {
public:
virtual void print() { std::cout << "base 2\n"; }
};
class derived : base1, base2 {}; // note: private inheritance
int main() {
derived *d = new derived;
base1 *b1 = (base1 *)d; // allowed
b1->print(); // prints "base 1"
base2 *b2 = (base2 *)d; // also allowed
b2->print(); // prints "base 2"
// base1 *bb1 = static_cast<base *>(d); // not allowed: base is inaccessible
// Using `reinterpret_cast` allows the code to compile.
// Unfortunately the result is different, and normally won't work.
base1 *bb2 = reinterpret_cast<base1 *>(d);
bb2->print(); // may cause nasal demons.
base2 *bb3 = reinterpret_cast<base2 *>(d);
bb3->print(); // likewise
return 0;
}
O código usando o reinterpret_cast
s irá compilar -- mas tentando usar o resultado (de pelo menos uma das duas) vai causar um grande problema.O reinterpret_cast
leva a base endereço do objeto derivado e tenta tratá-lo como se fosse especificado o tipo de objecto de base-e desde que (mais) um objecto de base, na verdade, pode existir nesse endereço, tentando tratá-lo como o outro pode/vai causar grandes problemas.Editar:Neste caso, as aulas são essencialmente idênticos, exceto pelo que eles impressão, então, apesar de qualquer coisa poderia acontecer, com a maioria dos compiladores, as duas últimas irá imprimir "da base de dados de 1".O reinterpret_cast leva tudo o que acontece naquele endereço e tenta usá-lo como o tipo especificado.Neste caso, eu (tentei) fazer o que fazer algo inofensivo, mas visível.No código real, o resultado provavelmente não será tão bonita.
O C-estilo elenco vai funcionar como um static_cast seria se o código tinha usado público herança, em vez de privada -- i.e.ele está ciente de onde a classe derivada de cada objeto de classe base "vida", e ajusta o resultado, de modo que cada ponteiro resultante irá funcionar porque foi ajustado para o ponto no lugar certo.
Outras dicas
Não, C-estilo lança pode atuar como reinterpret_cast
s, const-cast
s ou static_cast
s, dependendo da situação.É por isso que eles são desencorajados - você consulte um estilo de C expressos no código e precisa de olhar para os detalhes, para ver o que ele vai fazer.Por exemplo:
const char* source;
int* target = (int*)source;// - acts as const_cast and reinterpret_cast at once
//int* target = retinterpret_cast<int*>source;// - won't compile - can't remove const
Lembre-se, que uma constante de elenco pode estar atuando em algo diferente, em seguida, o original identificador:
void doit(const std::string &cs)
{
std::string &ms = const_cast<std::string &>(cs);
}
int main()
{
std::string s;
doit(s);
}
Assim, enquanto doit está lançando de si a const, neste exemplo subjacente de seqüência de caracteres não é constante, de modo nenhum comportamento indefinido.
Atualização
Ok, aqui está um exemplo melhor de quando usar const_cast não é completamente inútil.Nós começar com uma base de classe com uma função virtual que tem uma constante de parâmetro:
class base
{
public:
virtual void doit(const std::string &str);
};
e agora que você quiser substituir a função virtual.
class child : public base
{
public:
virtual void doit(const std::string &str)
{
std::string &mstr = const_cast<std::string &>(str);
}
};
Por causa da lógica/estrutura de seu código, você sabe que child::doit
só vai ser chamado com não-const cadeias de caracteres (e class base
não está sob o seu controle, portanto, você não pode modificar nem alterar a assinatura de child::doit
porque então ele não vai mais substituir base::doit
).Neste caso, é seguro para lançar fora const.
Sim, isso é arriscado.Talvez quando você escreve, é verdade que a execução nunca vai chegar child::doit
com um não-const string e o código é válido.Mas o que poderia alterar, mantendo o seu programa ou, talvez, quando você reconstruir e pegar a versão mais recente do class base
.
const_cast
é usado para remover const
a partir de um tipo.Ele também pode remover volatile
.Se o objeto realmente é const
em seguida, o resultado não pode ser gravado e ainda ser bem definidos de comportamento.Se, no entanto, ele é promovido para const
(sendo passado para um const T
função e, em seguida, const_cast
ing-lo de volta para nãoconst
é ok.( eu encontrei mais algumas informações aqui)
reinterpret_cast
não remover const
ou volatile
a partir de um tipo.
C-estilo de lança são realmente a marreta de programação - você basicamente informar ao compilador que o pino quadrado em cima de lá vai caber através deste orifício redondo, não importa o que.Nesse sentido, reinterpret_cast
é muito semelhante.
A principal vantagem que eu vejo em usar o C++estilo elenco operadores que eles permitem que você expresse sua intenção de melhor e permitir que o compilador ainda para alguns verificar a operação que você está pedindo a ele para realizar, ao invés do que o one-size-fits-all estilo C elenco.
Sobre const_cast
- muitas vezes você entrar na situação em que você está passando um objeto em torno de via constante de referência, simplesmente porque a API requer que você para fazer isso.Dizer, você tem função de X que as tarefas de um C-estilo de seqüência de caracteres:
void X(const char *str) { ... }
Dentro da função que você está passando o parâmetro para uma função C que espera um char *
, apesar de não alterar a seqüência de caracteres.A única forma de acomodar esta seria const_cast
str.
Eu teria muito cuidado com a utilização de qualquer tipo de elenco, muitas vezes, isso mostra que há algo não muito certo com o seu design, mas às vezes você tem que convencer o compilador que o peg ele olha não é tão quadrado como ele supõe.Só então você deve usar o elenco operadores.