在 C/C++ 中,不同的数据类型之间可以相互转换。无需用户指明如何转换的称为自动类型转换(隐式类型转换),需要用户显式地指明如何转换的称为强制类型转换。
自动类型转换示例:
int a = 6;
a = 7.5 + a;
编译器对 7.5 是作为 double 类型处理的,在求解表达式时,先将 a 转换为 double 类型,然后与 7.5 相加,得到和为 13.5。在向整型变量 a 赋值时,将 13.5 转换为整数 13,然后赋给 a。整个过程中,我们并没有告诉编译器如何去做,编译器使用内置的规则完成数据类型的转换。
强制类型转换示例:
int n = 100;
int *p1 = &n;
float *p2 = (float*)p1;
p1 是int *类型,它指向的内存里面保存的是整数,p2 是float *类型,将 p1 赋值给 p2 后,p2 也指向了这块内存,并把这块内存中的数据作为小数处理。我们知道,整数和小数的存储格式大相径庭,将整数作为小数处理非常荒诞,可能会引发莫名其妙的错误,所以编译器默认不允许将 p1 赋值给 p2。但是,使用强制类型转换后,编译器就认为我们知道这种风险的存在,并进行了适当的权衡,所以最终还是允许了这种行为。
关于整数和小数在内存中的存储格式,请猛击《整数在内存中是如何存储的》《小数在内存中是如何存储的》。
不管是自动类型转换还是强制类型转换,前提必须是编译器知道如何转换,例如,将小数转换为整数会抹掉小数点后面的数字,将int *转换为float *只是简单地复制指针的值,这些规则都是编译器内置的,我们并没有告诉编译器。
换句话说,如果编译器不知道转换规则就不能转换,使用强制类型也无用,请看下面的例子:
#include
using namespace std;
//复数类
class Complex{
public:
Complex(): m_real(0.0), m_imag(0.0){ }
Complex(double real, double imag): m_real(real), m_imag(imag){ }
public:
friend ostream & operator<
private:
double m_real; //实部
double m_imag; //虚部
};
//重载>>运算符
ostream & operator<
out << c.m_real <
return out;
}
int main(){
Complex a(10.0, 20.0);
a = (Complex)25.5; //错误,转换失败
return 0;
}
25.5 是实数,a 是复数,将 25.5 赋值给 a 后,我们期望 a 的实部变为 25.5,而虚部为 0。但是,编译器并不知道这个转换规则,这超出了编译器的处理能力,所以转换失败,即使加上强制类型转换也无用。
幸运的是,C++ 允许我们自定义类型转换规则,用户可以将其它类型转换为当前类类型,也可以将当前类类型转换为其它类型。这种自定义的类型转换规则只能以类的成员函数的形式出现,换句话说,这种转换规则只适用于类。
本节我们先讲解如何将其它类型转换为当前类类型,下节再讲解如何将当前类类型转换为其它类型。
转换构造函数
将其它类型转换为当前类类型需要借助转换构造函数(Conversion constructor)。转换构造函数也是一种构造函数,它遵循构造函数的一般规则。转换构造函数只有一个参数。
仍然以 Complex 类为例,我们为它添加转换构造函数:
#include
using namespace std;
//复数类
class Complex{
public:
Complex(): m_real(0.0), m_imag(0.0){ }
Complex(double real, double imag): m_real(real), m_imag(imag){ }
Complex(double real): m_real(real), m_imag(0.0){ } //转换构造函数
public:
friend ostream & operator<
private:
double m_real; //实部
double m_imag; //虚部
};
//重载>>运算符
ostream & operator<
out << c.m_real <
return out;
}
int main(){
Complex a(10.0, 20.0);
cout<
a = 25.5; //调用转换构造函数
cout<