一、隐式转换
1.算术转换
1)条件表达式中,非布尔类型变量转化为布尔类型。0值转换为false,非0值转换为true。
2)赋值语句中,右值类型转换为左值类型。
举个例子:
bool b = 42;
int i=b;
第一句语句中,42转换成true,第二句语句中,b的值true转换成1,赋给i。
3)算数表达式中的类型转换分为以下几步:
① 整型提升
凡是类型中所有的值可以全部存入int类型的变量(如bool,char,signed char,unsigned char,short,unsigned short……),转换为int。
对于类型中所有的值无法全部存入int类型的变量(如wchar_t,char16_t,char32_t……),转换为可以包含它所有值的最小的类型,如int,unsigned int,long,unsigned long,long long,unsigned long long等。
② 有、无符号的情况
先进行整型提升。
在整型提升之后:
若符号相同,则将小类型转换为大类型。
若无符号类型大于有符号类型,则将有符号类型转换为无符号类型。
若有符号类型大于无符号类型,则分两种情况:
无符号类型中的所有值都可以存到有符号类型中:将无符号类型转换为有符号类型。
否则,将有符号类型转换为无符号类型。
举个例子:
long + unsigned int
如果sizeof(int) == sizeof(long),此时无符号类型unsigned int中的所有值无法全部存到long中,所以将long转换为unsigned int。
如果sizeof(int) < sizeof(long),则就将unsigned int 转换为long。
由于无符号数恒大于等于0,将有符号数转换为无符号数会导致错误。
2. 数组名转换为指针
数组名会被隐式地转换为指向数组首元素的指针。
3. 指针转换
1)指针转换为布尔类型
char *cp = get_string();
if (cp)......
2)指向非const 类型数据的指针转换为指向const类型的指针
int i;
const int *p = &i;
二、显式转换
C++中提供4种显式类型转换方式。
1. static_cast
常用的转换方式,使用举例如下:
double slope = static_cast<double>(j)/i;
2. dynamic_cast
将基类的指针或引用安全地转换到派生类的指针或引用。
如果转换失败,指针转换时返回0,引用转换时抛出bad_cast异常。
使用举例:
if (Derived dp = dynamic_cast<Derived>(bp)){......}
else {......}
3. const_cast
具有去常量性的作用,可以将指向const类型的指针转换为指向非const类型的指针,举例如下:
const char *pc;
char *p = const_cast<char*>(pc);
4. reinterpret_cast
一种重编译的类型转换,使用较少。
三、类类型转换
1. 转换运算符
类类型转换运算符格式如下:
operator type() const;
无返回类型,无参数,声明为类的const成员函数。
可以通过加上explicit关键字来防止自动的类型转换。举例如下
class Smalllnt{
public:
explicit operator int() const{
return val;
}
}
此时,类似si + 3 的表达式是错误的,编译器不会自动把si转换成int类型。必须显式转换:static_cast<int>(si) + 3。
2. 二义性
在类类型转换中,可能会出现一些二义性。
1)构造函数与转换运算符的二义性
比如以下语句:
class A: A(const B&);
class B:operator A() const;
A f(const A&);
B b;
A a = f(b);
此时,编译器不知是将B 类型的b 调用类型转换运算符转换为A,还是调用A的构造函数。正确的做法是显式写出,写法如下:
A a1 = f(b.operator A());
A a2 = f(A(b));
2) 涉及算术类型的二义性
在两个转换源或转换目标均为算术类型时,很容易出现二义性。比如以下语句中:
class A{
A(int = 0);
A(double);
operator int() const;
operator double() const;
}
void f2(long double);
A a;
f2(a);
long lg;
A a2(lg);
在f2(a)中,编译器不知是将a转换为int还是double类型。
在A a2(lg)中,编译器也不知是将lg转换成int还是double类型。
可见,我们要避免两个转换源或转换目标均为算术类型的情况。
3) 重载函数二义性
在重载函数中,也容易产生二义性
class C { C(int);}
class E { E(double);}
void manip2(const C&);
void manip2(const D&);
manip2(10);
在manip2(10)中,编译器也不知是将10转换成C还是D,产生了二义性。
4) 运算符重载与类类型转换的冲突
重载的运算符容易与类类型转换产生冲突
class Smalllnt{
friend Smalllnt operator+(const Smalllnt&,const Smalllnt&){...}
public:
Smalllnt(int = 0);
operator int() const{return val;}
private:
size_t val;
}
Smalllnt s1,s2;
Smalllnt s3 = s1 + s2;
int i = s3 + 0;
在 int i = s3 + 0; 中,编译器也不知是将0转换为Smalllnt类型,然后调用重载的+运算符,还是将s3转换成int类型,导致了冲突。