Conversões de tipo e a segurança de tipos (C++)
Este documento identifica problemas comuns de conversão de tipos e descreve como você pode evitar no seu código C++.
Quando você escrever o programa de c++, é importante garantir que é com segurança.Isso significa que cada variável, argumento da função, e valor de retorno da função são armazenando um tipo aceitável de dados, e que operações envolvendo valores de diferentes tipos “fazem sentido” e não causam perda de dados, a interpretação incorreta de padrão de bits, ou o dano de memória.Um programa que nunca explícita ou implicitamente converte valores de um tipo para outro é com segurança por definição.No entanto, conversões de tipos, mesmo conversões não seguro, às vezes são necessárias.Por exemplo, você pode ter que armazenar o resultado de uma operação de ponto flutuante em uma variável do tipo int, ou você pode precisar passar o valor em int sem sinal para uma função que utiliza intassinado.Dois exemplos a seguir ilustram conversões porque não seguros podem causar perda de dados ou novamente a interpretação de um valor.
Quando o compilador detecta uma conversão não seguro, emite um erro ou aviso.Um erro de compilação; um aviso permite que a compilação continue mas indica um possível erro no código.No entanto, mesmo se seu programa compila sem avisos, ainda pode conter código que resulta em conversões implícitas de tipo que produz resultados incorretos.Erros de tipo podem também ser apresentados por conversões explícitas, ou por conversão, no código.
Conversões implícitas de tipo
Quando uma expressão contém operandos de diferentes tipos internos, e nenhuma conversão explícita estiverem presentes, o compilador usa conversões padrões internas para converter um dos operandos de modo que os tipos correspondem.O compilador tenta as conversões em uma sequência bem definido até que uma foi bem-sucedida.Se a conversão selecionada é uma promoção, o compilador não emite um aviso.Se a conversão é redutoras, o compilador emite um aviso sobre a possível perda de dados.Se a perda de dados real ocorre depende dos valores reais envolvidos, mas é recomendável que você manipula esse aviso como um erro.Se um tipo definido pelo usuário estiver envolvido, o compilador tenta usar conversões que você especificou na definição de classe.Se não pode localizar uma conversão aceitável, o compilador emite um erro e não compilar o programa.Para obter mais informações sobre regras que determinam as conversões padrão, consulte Conversões padrão.Para obter mais informações sobre as conversões definidos pelo usuário, consulte Conversões definidas pelo usuário (C++/CLI).
Conversões de ampliação (promoção)
Em uma conversão de ampliação, um valor em uma variável menor é atribuído a uma variável maior sem perda de dados.Como conversões ampliadoras sempre são seguras, o compilador as executa no e silenciosamente não emite avisos.As seguintes conversões está ampliando conversões.
From |
Para |
---|---|
Qualquer tipo integral com ou sem sinal, exceto long long ou __int64 |
double |
bool ou char |
Qualquer outro tipo interno |
short ou wchar_t |
int, long, long long |
int, long |
long long |
float |
double |
Restrição (coerção)
O compilador executa conversões redutoras implicitamente, mas adverte-o sobre potencial perda de dados.Usa esses avisos muito seriamente.Se tiver certeza que nenhuma perda de dados ocorrerá porque os valores na variável maior caberão na variável sempre menor, adicione uma conversão explícita de modo que o compilador não emita um aviso.Se você não tiver certeza que a conversão é segura, adicionar ao seu código algum tipo de verificação de tempo de execução para possível perda de dados de forma de modo que não cause seu programa resultados incorretos do produto.Para sugestões sobre como manipular esse cenário, consulte Como: manipular restringir conversões (C++).
Qualquer conversão de um tipo de ponto flutuante em um tipo integral é uma conversão redutora porque a parte fracional do valor de ponto flutuante é descartada e perdida.
O exemplo de código a seguir mostra alguns conversões redutoras implícitas, e os avisos do compilador envia para eles.
int i = INT_MAX + 1; //warning C4307:'+':integral constant overflow
wchar_t wch = 'A'; //OK
char c = wch; // warning C4244:'initializing':conversion from 'wchar_t'
// to 'char', possible loss of data
unsigned char c2 = 0xfffe; //warning C4305:'initializing':truncation from
// 'int' to 'unsigned char'
int j = 1.9f; // warning C4244:'initializing':conversion from 'float' to
// 'int', possible loss of data
int k = 7.7; // warning C4244:'initializing':conversion from 'double' to
// 'int', possible loss of data
Assinado - conversões sem-sinal
Um tipo integral assinado e suas contrapartes sem sinal são sempre o mesmo tamanho, mas difere em como o padrão de bits é interpretado para a transformação de valor.O exemplo de código demonstra o que acontece quando o mesmo padrão de bits é interpretado como um valor assinado e como um valor sem sinal.O padrão de bits armazenado no num e num2 nunca alterações do que é mostrado na ilustração anterior.
using namespace std;
unsigned short num = numeric_limits<unsigned short>::max(); // #include <limits>
short num2 = num;
cout << "unsigned val = " << num << " signed val = " << num2 << endl;
// Prints: unsigned val = 65535 signed val = -1
// Go the other way.
num2 = -1;
num = num2;
cout << "unsigned val = " << num << " signed val = " << num2 << endl;
// Prints: unsigned val = 65535 signed val = -1
Observe que os valores reinterpreted em ambas as direções.Se seu programa produz resultados ímpares em que o sinal de valor seja invertido do que você espera, procure conversões implícitas entre tipos com sinal e sem sinal integral.No exemplo, o resultado da expressão (0 – 1) é convertido implicitamente de int a unsigned int quando armazenado em num.Isso faz com que o padrão de bits para ser reinterpreted.
unsigned int u3 = 0 - 1;
cout << u3 << endl; // prints 4294967295
O compilador não avisará sobre conversões implícitas entre tipos integrais assinados e sem sinal.Como consequência, recomendamos que você impede conversões assinar-à- sem sinal completamente.Se você não pode evitar os, então adicionar ao seu código uma verificação de tempo de execução como detectar se o valor que está sendo convertido é maior ou igual a zero e menor ou igual ao valor máximo de tipo com sinal.Os valores no intervalo transferirão da assinatura sem sinal ou de sem assinatura a ser assinado sem reinterpreted.
Conversões do ponteiro
Em muitas expressões, se AC - a matriz de estilo? a implicitamente a um ponteiro para o primeiro elemento na matriz, e as conversões constantes pode acontecer silenciosamente.Embora isso seja conveniente, também é potencialmente a probabilidade de erros.Por exemplo, o exemplo mal criado de código parece sem-sentido no entanto, irá criar no Visual C++ e produz um resultado de “p”.Primeiro, o literal constante de cadeia de caracteres de “ajuda” é convertido em char* que aponta para o primeiro elemento da matriz; o ponteiro é incrementado em seguida por três elementos de modo que ele agora aponta para o último elemento “p”.
char* s = "Help" + 3;
Conversões explícitas (conversões)
Usando uma operação de conversão, você pode instruir o compilador para converter um valor de um tipo para outro tipo.O compilador gerará um erro em alguns casos se os dois tipos não são completamente relacionado, mas em outros casos não irá gerar um erro se a operação não é segura.O uso converte com parcimônia porque qualquer conversão de um tipo para outro é uma fonte potencial de erro do programa.No entanto, as conversões são necessárias às vezes, e nem todas as conversões são igualmente perigosas.Um uso eficiente de uma conversão é quando seu código executar uma conversão redutora e você souber que a conversão não está causando seu programa resultados incorretos do produto.Na verdade, isso informa o compilador que você souber o qual você está fazendo e parar de incomodar com avisos sobre ele.Outro uso é converter de uma classe derivada ponteiro-à- a uma classe de ponteiro-à- base.Outro uso é converter possam const- Ness de uma variável para passar para uma função que requer um argumento não deconst .A maioria dessas operações de conversão envolvem alguns risco.
Na programação ctype de estilo, o mesmo operador cast ctype -- estilo é usado para todos os tipos das conversões.
(int) x; // old-style cast, old-style syntax
int(x); // old-style cast, functional syntax
O operador cast ctype -- estilo é idêntico ao operador de chamada () e é como consequência imperceptível no código e fácil de ignorar.Ambos são incorretos porque eles são difíceis de reconhecer duma olhada ou procurar por, e são díspares o suficiente para chamar qualquer combinação de static, de const, e de reinterpret_cast.Figurar para fora o que uma conversão desatualizado faz realmente pode ser difícil e sujeita a erros.Para todos esses motivos, quando uma conversão é exigida, recomendamos que você usa um dos seguintes operadores cast C++, que são em alguns casos com significativamente mais seguros, e que expressam muito mais explicitamente a intenção de programação:
static_cast, para as conversões que são verificadas somente em tempo de compilação.static_cast retorna um erro se o compilador detecta que você está tentando converter entre os tipos que são completamente tipo.Você também pode usá-lo para converter entre a ponteiro-à- base e ponteiro-à- derivada de, mas o compilador não pode sempre determinar se tais conversões serão segurança em tempo de execução.
double d = 1.58947; int i = d; // warning C4244 possible loss of data int j = static_cast<int>(d); // No warning. string s = static_cast<string>(d); // Error C2440:cannot convert from // double to std:string // No error but not necessarily safe. Base* b = new Base(); Derived* d2 = static_cast<Derived*>(b);
Para obter mais informações, consulte static_cast.
dynamic_cast, para o cofre, o tempo de execução verificado conversões de ponteiro-à- base a ponteiro-à- derivado.dynamic_cast é mais segura que static_cast para entradas de ar, mas a verificação de tempo de execução provoca uma sobrecarga.
Base* b = new Base(); // Run-time check to determine whether b is actually a Derived* Derived* d3 = dynamic_cast<Derived*>(b); // If b was originally a Derived*, then d3 is a valid pointer. if(d3) { // Safe to call Derived method. cout << d3->DoSomethingMore() << endl; } else { // Run-time check failed. cout << "d3 is null" << endl; } //Output: d3 is null;
Para obter mais informações, consulte dynamic_cast.
const_cast, para converter possam const- Ness de uma variável, ou converter uma variável não deconst para ser const.Convertendo constestá faltando - Ness usando o operador apenas tão a probabilidade de erros quanto estiver usando a energia AC - estilize a conversão, exceto que com const-cast é menos provável que você executar até a conversão.Às vezes você tem que converter possam const- Ness de uma variável, por exemplo, para passar uma variável de const a uma função que recebe um parâmetro deconst não-.O exemplo a seguir mostra como fazer isto:
void Func(double& d) { ... } void ConstCast() { const double pi = 3.14; Func(const_cast<double&>(pi)); //No error. }
Para obter mais informações, consulte const_cast.
reinterpret_cast, para conversões entre tipos não relacionados como pointer a int.
Observação Este operador cast não é tão frequentemente usado como o outro, e não é garantido ser portátil a outros compiladores.
O exemplo a seguir ilustra como reinterpret_cast difere de static_cast.
const char* str = "hello"; int i = static_cast<int>(str);//error C2440: 'static_cast' : cannot // convert from 'const char *' to 'int' int j = (int)str; // C-style cast. Did the programmer really intend // to do this? int k = reinterpret_cast<int>(str);// Programming intent is clear. // However, it is not 64-bit safe.
Para obter mais informações, consulte reinterpret_cast operador.
Consulte também
Conceitos
Sistema de tipo C++ (guia de programação do C++ moderno)