Conversões padrão
A linguagem C++ define conversões entre seus tipos básicos. Ela também define conversões para o ponteiro, referência e tipos derivados de ponteiro ao membro. Essas conversões são chamadas de conversões padrão.
Esta seção aborda as seguintes conversões padrão:
Promoções de integral
Conversões de integral
Conversões flutuantes
Conversões flutuantes e integrais
Conversões aritméticas
Conversões de ponteiro
Conversões de referência
Conversões de ponteiro ao membro
Observação
Os tipos definidos pelo usuário podem especificar suas próprias conversões. A conversão de tipos definidos pelo usuário está coberta em Construtores e em Construtores e Conversões.
O código a seguir causa conversões (neste exemplo, promoções de integral):
long long_num1, long_num2;
int int_num;
// int_num promoted to type long prior to assignment.
long_num1 = int_num;
// int_num promoted to type long prior to multiplication.
long_num2 = int_num * long_num2;
O resultado de uma conversão é um l-value somente se ela produzir um tipo de referência. Por exemplo, uma conversão definida pelo usuário declarada como operator int&()
retorna uma referência e é um valor l. No entanto, uma conversão declarada com operator int()
retorna um objeto e não é um valor l.
Promoções de integral
Objetos de um tipo integral podem ser convertidos em outro tipo integral mais amplo, ou seja, um tipo que pode representar um conjunto maior de valores. Esse tipo de conversão em expansão é chamado de promoção integral. Com a promoção integral, você poderá usar os seguintes tipos em uma expressão sempre que outro tipo integral puder ser usado:
Objetos, literais e constantes do tipo
char
eshort int
Tipos de enumeração
campos de bits
int
Enumeradores
As promoções de C++ são "preservadoras de valor", pois o valor após a promoção tem a garantia de ser o mesmo que o valor anterior da promoção. Nas promoções de preservação de valores, os objetos de tipos integrais mais curtos (como campos de bits ou objetos do tipo char
) serão promovidos para o tipo int
se int
puder representar a gama completa do tipo original. Se int
não puder representar o intervalo completo de valores, o objeto será promovido para o tipo unsigned int
. Embora essa estratégia seja a mesma usada pelo Padrão C, as conversões de preservação de valor não preservam a "assinatura" do objeto.
As promoções de preservação de valores e as promoções que preservam o signedness normalmente geram os mesmos resultados. No entanto, poderão produzir resultados diferentes se o objeto promovido aparecer como:
Um operando de
/
,%
,/=
,%=
,<
,<=
,>
ou>=
Esses operadores dependem do sinal para determinar o resultado. As promoções de preservação de valor e de preservação de sinal produzem resultados diferentes quando aplicadas a esses operandos.
O operando esquerdo de
>>
ou>>=
Esses operadores tratam quantidades com sinal e sem sinal de maneira diferente em uma operação de turno. Para quantidades com sinal, uma operação de deslocamento para a direita propaga o bit de sinal nas posições de bit desocupadas, enquanto as posições de bit desocupadas são preenchidas com zero em quantidades sem sinal.
Um argumento para uma função sobrecarregada, ou o operando de um operador sobrecarregado, que depende da assinatura do tipo de operando para correspondência de argumentos. Para obter mais informações sobre como definir operadores sobrecarregados, consulte Operadores sobrecarregados.
Conversões de integral
Conversões integrais são conversões entre tipos integrais. Os tipos integrais são char
, short
(ou short int
), int
, long
e long long
. Esses tipos podem ser qualificados com ou signed
ou unsigned
e unsigned
podem ser usados como abreviação de unsigned int
.
Assinado para sem sinal
Os objetos de tipos integrais com sinal podem ser convertidos nos tipos sem sinal correspondentes. Quando essas conversões ocorrem, o padrão de bits real não altera. No entanto, a interpretação dos dados altera. Considere este código:
#include <iostream>
using namespace std;
int main()
{
short i = -3;
unsigned short u;
cout << (u = i) << "\n";
}
// Output: 65533
No exemplo acima, um signed short
, i
, é definido e inicializado como um número negativo. A expressão (u = i)
faz com que i
seja convertido em um unsigned short
antes da atribuição a u
.
Sem sinal para com sinal
Os objetos de tipos integrais sem sinal podem ser convertidos nos tipos com sinal correspondentes. No entanto, se o valor sem sinal estiver fora do intervalo representável do tipo com sinal, o resultado não terá o valor correto, conforme demonstrado no exemplo a seguir:
#include <iostream>
using namespace std;
int main()
{
short i;
unsigned short u = 65533;
cout << (i = u) << "\n";
}
//Output: -3
No exemplo acima, u
é um objeto integral unsigned short
que deve ser convertido em uma quantidade com sinal para avaliar a expressão (i = u)
. Como o valor não pode ser representado adequadamente em um signed short
, os dados são interpretados incorretamente conforme mostrado.
Conversões de ponto flutuante
Um objeto de um tipo flutuante pode ser convertido com segurança em um tipo flutuante mais preciso, ou seja, a conversão não causa perda de significância. Por exemplo, as conversões de float
para double
ou de double
para long double
são seguras e o valor não é alterado.
Um objeto de tipo flutuante também poderá ser convertido em um tipo menos preciso, se estiver em um intervalo representável por esse tipo. (Consulte Limites Flutuantes para os intervalos de tipos flutuante.) Se o valor original não for representável com precisão, ele poderá ser convertido no valor representável imediatamente superior ou inferior. O resultado será indefinido se esse valor não existir. Considere o seguinte exemplo:
cout << (float)1E300 << endl;
O valor máximo representável por tipo float
é 3.402823466E38, que é um número muito menor do que 1E300. Portanto, o número é convertido para infinito e o resultado é "inf".
Conversões entre tipos de ponto flutuante e integral
Algumas expressões podem fazer com que os objetos de tipo flutuante sejam convertidos em tipos integrais, ou vice-versa. Quando um objeto do tipo integral é convertido em um tipo flutuante e o valor original não é representável exatamente, o resultado é o valor representável imediatamente superior ou inferior.
Quando um objeto do tipo flutuante é convertido em um tipo integral, a parte fracionária é truncada ou arredondada para zero. Um número como 1,3 é convertido em 1 e -1,3 é convertido em -1. Se o valor truncado for maior que o valor representável mais alto ou menor que o valor representável mais baixo, o resultado será indefinido.
Conversões aritméticas
Muitos operadores binários (abordados em Expressões com operadores binários) causam conversões de operandos e produzem resultados da mesma maneira. As conversões causadas por esses operadores são chamadas de conversões aritméticas usuais. As conversões aritméticas de operandos que possuem diferentes tipos nativos são feitas conforme mostrado na tabela a seguir. Os tipos Typedef se comportam de acordo com seus tipos nativos subjacentes.
Condições para conversão de tipo
Condições atendidas | Conversão |
---|---|
Qualquer um dos operandos é do tipo long double . |
Outro operando é convertido para o tipo long double . |
Condição anterior não atendida e qualquer um dos operandos é do tipo double . |
Outro operando é convertido para o tipo double . |
Condições precedentes não atendidas e qualquer um dos operandos é do tipo float . |
Outro operando é convertido para o tipo float . |
As condições anteriores não foram atendidas (nenhum dos operandos é de tipo flutuante). | Operandos obtêm promoções integrais da seguinte forma: − Se qualquer um dos operandos for do tipo unsigned long , o outro operando será convertido para o tipo unsigned long .− Se a condição anterior não for atendida e se qualquer um dos operandos for do tipo long e o outro do tipo unsigned int , ambos os operandos serão convertidos para o tipo unsigned long .− Se as duas condições anteriores não forem atendidas e se qualquer um dos operandos for do tipo long , o outro operando será convertido para o tipo long .− Se as três condições anteriores não forem atendidas e se um dos operandos for do tipo unsigned int , o outro operando será convertido para o tipo unsigned int .− Se nenhuma das condições anteriores for atendida, ambos os operandos serão convertidos para o tipo int . |
O código a seguir ilustra as regras de conversão descritas na tabela:
double dVal;
float fVal;
int iVal;
unsigned long ulVal;
int main() {
// iVal converted to unsigned long
// result of multiplication converted to double
dVal = iVal * ulVal;
// ulVal converted to float
// result of addition converted to double
dVal = ulVal + fVal;
}
A primeira instrução no exemplo acima mostra a multiplicação de dois tipos integrais, iVal
e ulVal
. A condição atendida é que nenhum dos operandos seja do tipo flutuante e um operando seja do tipo unsigned int
. Assim, o outro operando, iVal
, é convertido para o tipo unsigned int
. Em seguida, o resultado é atribuído a dVal
. A condição atendida aqui é que um operando seja do tipo double
, então, o resultado unsigned int
da multiplicação é convertido para o tipo double
.
A segunda instrução no exemplo anterior mostra a adição de um float
e um tipo integral: fVal
e ulVal
. A variável ulVal
é convertida para o tipo float
(terceira condição da tabela). O resultado da adição é convertido para o tipo double
(segunda condição da tabela) e atribuído a dVal
.
Conversões de ponteiro
Os ponteiros podem ser convertidos durante a atribuição, a inicialização, a comparação e outras expressões.
Ponteiro para classes
Há dois casos nos quais um ponteiro para uma classe pode ser convertido em um ponteiro para uma classe base.
O primeiros é quando a classe base especificada estiver acessível e a conversão for inequívoca. Para obter mais informações sobre referências ambíguas de classe base, consulte Várias classes base.
O acesso a uma classe base depende do tipo de herança usado na derivação. Considere a herança ilustrada na figura a seguir:
O diagrama mostra a classe base A. A classe B herda de A por meio de público privado protegido. A classe C herda de B via pública B.
Gráfico de herança ilustrando a acessibilidade de classe base
A tabela a seguir mostra a acessibilidade da classe base para a situação ilustrada na figura.
Tipo de função | Derivação | Conversão deB* para A* legal? |
---|---|---|
Função externa (fora do escopo da classe) | Privados | Não |
Protegido | Não | |
Setor Público | Sim | |
Função membro B (no escopo de B) | Privado | Sim |
Protegido | Sim | |
Público | Sim | |
Função membro C (no escopo de C) | Privados | Não |
Protegido | Sim | |
Público | Sim |
O segundo caso em que um ponteiro para uma classe pode ser convertido em um ponteiro para uma classe base é quando uma conversão de tipo explícita. Para obter mais informações sobre conversões de tipo explícito, consulte Operador de conversão de tipo explícito.
O resultado dessa conversão é um ponteiro para o subobject, a parte do objeto que é completamente descrita pela classe base.
O código a seguir define duas classes, A
e B
, onde B
é derivado de A
. (Para obter mais informações sobre herança, consulte Classes derivadas.) Em seguida, define bObject
, um objeto do tipo B
, e dois ponteiros (pA
e pB
) que apontam para o objeto.
// C2039 expected
class A
{
public:
int AComponent;
int AMemberFunc();
};
class B : public A
{
public:
int BComponent;
int BMemberFunc();
};
int main()
{
B bObject;
A *pA = &bObject;
B *pB = &bObject;
pA->AMemberFunc(); // OK in class A
pB->AMemberFunc(); // OK: inherited from class A
pA->BMemberFunc(); // Error: not in class A
}
O ponteiro pA
é do tipo A *
, que pode ser interpretado como "ponteiro para um objeto do tipo A
". Os membros de bObject
(como BComponent
e BMemberFunc
) são exclusivos do tipo B
e, portanto, são inacessíveis por meio de pA
. O ponteiro pA
permite acesso somente às características (funções membro e dados) do objeto que são definidas na classe A
.
Ponteiro para função
Um ponteiro para uma função poderá ser convertido para o tipo void *
, se o tipo void *
for grande o suficiente para conter esse ponteiro.
Ponteiro para nulo
Ponteiros para o tipo void
podem ser convertidos em ponteiros para qualquer outro tipo, mas apenas com um tipo explícito (diferente de C). Um ponteiro para qualquer tipo pode ser convertido implicitamente em um ponteiro para o tipo void
. Um ponteiro para um objeto incompleto de um tipo pode ser convertido em um ponteiro para void
(implicitamente) e vice-versa (explicitamente). O resultado dessa conversão é igual ao valor do ponteiro original. Um objeto é considerado incompleto se ele é declarado mas não há informações suficientes disponíveis para determinar o seu tamanho ou a sua classe base.
Um ponteiro para qualquer objeto que não const
seja ou volatile
possa ser convertido implicitamente em um ponteiro do tipo void *
.
Ponteiros const e volatile
O C++ não fornece uma conversão padrão de um tipo const
ou volatile
para um tipo que não seja const
ou volatile
. No entanto, qualquer tipo de conversão pode ser especificado usando as conversões de tipos explícitas (inclusive as conversões que não são seguras).
Observação
Ponteiros C++ para membros, exceto ponteiros para membros estáticos, são diferentes de ponteiros normais e não têm as mesmas conversões padrão. Os ponteiros para os membros estáticos são ponteiros normais e têm as mesmas conversões que ponteiros normais.
conversões de ponteiro nulo
Uma expressão constante integral avaliada como zero, ou uma expressão convertida em um tipo de ponteiro, é convertida em um ponteiro chamado de ponteiro nulo. Esse ponteiro sempre compara desigual a um ponteiro para qualquer função ou objeto válido. Uma exceção são ponteiros para objetos baseados, que podem ter o mesmo deslocamento e apontar para objetos diferentes.
No C++11, o tipo nullptr deverá ser preferencial para o ponteiro nulo no estilo C.
Conversões de expressão de ponteiro
Qualquer expressão com um tipo de matriz pode ser convertida em um ponteiro do mesmo tipo. O resultado da conversão é um ponteiro para o primeiro elemento da matriz. O exemplo a seguir demonstra essa conversão:
char szPath[_MAX_PATH]; // Array of type char.
char *pszPath = szPath; // Equals &szPath[0].
Uma expressão que resulta em uma função que retorna um tipo específico é convertido em um ponteiro para uma função que retorna esse tipo, exceto quando:
A expressão é usada como um operando para o operador de endereço (&).
A expressão é usada como um operando para o operador function-call.
Conversões de referência
Uma referência a uma classe poderá ser convertida em uma referência a uma classe base nestes casos:
A classe base especificada é acessível.
A conversão é inequívoca. (Para obter mais informações sobre referências ambíguas de classe base, consulte Várias classes base.)
O resultado da conversão é um ponteiro para o subobjeto que representa a classe base.
Ponteiro para membro
Os ponteiros para membros de classe podem ser convertidos durante a atribuição, a inicialização, a comparação e outras expressões. Esta seção descreve as seguintes conversões de ponteiros para membros:
Ponteiro para membro da classe base
Um ponteiro para um membro de uma classe base pode ser convertido em um ponteiro para um membro de uma classe derivada dela, quando as seguintes condições são atendidas:
A conversão inversa, de ponteiro para classe derivada em ponteiro para classe base, é acessível.
A classe derivada não herda virtualmente da classe base.
Quando o operando esquerdo é um ponteiro para um membro, o operando direito deve ser do tipo ponteiro para membro ou ser uma expressão de constante que é avaliada como 0. Essa atribuição só é válida nos seguintes casos:
O operando direito é um ponteiro para um membro da mesma classe que o operando esquerdo.
O operando esquerdo é um ponteiro para um membro de uma classe derivada, de forma pública e sem ambiguidade, da classe do operando direito.
ponteiro nulo para conversões de membros
Uma expressão constante integral que é avaliada como zero é convertida em um ponteiro nulo. Esse ponteiro sempre compara desigual a um ponteiro para qualquer função ou objeto válido. Uma exceção são ponteiros para objetos baseados, que podem ter o mesmo deslocamento e apontar para objetos diferentes.
O código a seguir ilustra a definição de um ponteiro para o membro i
na classe A
. O ponteiro, pai
, é inicializado como 0, que é o ponteiro nulo.
class A
{
public:
int i;
};
int A::*pai = 0;
int main()
{
}
Confira também
Comentários
https://aka.ms/ContentUserFeedback.
Brevemente: Ao longo de 2024, vamos descontinuar progressivamente o GitHub Issues como mecanismo de feedback para conteúdos e substituí-lo por um novo sistema de feedback. Para obter mais informações, veja:Submeter e ver comentários