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 <iostream> 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<<(ostream &out, Complex &c); //友元函数 private: double m_real; //实部 double m_imag; //虚部 }; //重载>>运算符 ostream & operator<<(ostream &out, Complex &c){ out << c.m_real <<" + "<< c.m_imag <<"i";; 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。但是,编译器并不知道这个转换规则,这超出了编译器的处理能力,所以转换失败,即使加上强制类型转换也无用。
#include <iostream> 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<<(ostream &out, Complex &c); //友元函数 private: double m_real; //实部 double m_imag; //虚部 }; //重载>>运算符 ostream & operator<<(ostream &out, Complex &c){ out << c.m_real <<" + "<< c.m_imag <<"i";; return out; } int main(){ Complex a(10.0, 20.0); cout<<a<<endl; a = 25.5; //调用转换构造函数 cout<<a<<endl; return 0; }运行结果:
Complex(double real);
就是转换构造函数,它的作用是将 double 类型的参数 real 转换成 Complex 类的对象,并将 real 作为复数的实部,将 0 作为复数的虚部。这样一来,a = 25.5;
整体上的效果相当于:
a.Complex(25.5);
将赋值的过程转换成了函数调用的过程。Complex c1(26.4); //创建具名对象 Complex c2 = 240.3; //以拷贝的方式初始化对象 Complex(15.9); //创建匿名对象 c1 = Complex(46.9); //创建一个匿名对象并将它赋值给 c1在以拷贝的方式初始化对象时,编译器先调用转换构造函数,将 240.3 转换为 Complex 类型(创建一个 Complex 类的匿名对象),然后再拷贝给 c2。
+
运算符进行了重载,使之能进行两个 Complex 类对象的相加,那么下面的语句也是正确的:
Complex c1(15.6, 89.9); Complex c2; c2 = c1 + 29.6; cout<<c2<<endl;在进行加法运算符时,编译器先将 29.6 转换为 Complex 类型(创建一个 Complex 类的匿名对象)再相加。
int main(){ Complex c1 = 100; //int --> double --> Complex cout<<c1<<endl; c1 = 'A'; //char --> int --> double --> Complex cout<<c1<<endl; c1 = true; //bool --> int --> double --> Complex cout<<c1<<endl; Complex c2(25.8, 0.7); //假设已经重载了+运算符 c1 = c2 + 'H' + true + 15; //将char、bool、int都转换为Complex类型再运算 cout<<c1<<endl; return 0; }运行结果:
Complex(); //没有参数
Complex(double real, double imag); //两个参数
Complex(const Complex &c);
Complex(double real);
Complex c1(); //调用Complex() Complex c2(10, 20); //调用Complex(double real, double imag) Complex c3(c2); //调用Complex(const Complex &c) Complex c4(25.7); //调用Complex(double real)这些代码都体现了构造函数的本意——在创建对象时初始化对象。
#include <iostream> using namespace std; //复数类 class Complex{ public: Complex(double real = 0.0, double imag = 0.0): m_real(real), m_imag(imag){ } public: friend ostream & operator<<(ostream &out, Complex &c); //友元函数 private: double m_real; //实部 double m_imag; //虚部 }; //重载>>运算符 ostream & operator<<(ostream &out, Complex &c){ out << c.m_real <<" + "<< c.m_imag <<"i";; return out; } int main(){ Complex a(10.0, 20.0); //向构造函数传递 2 个实参,不使用默认参数 Complex b(89.5); //向构造函数传递 1 个实参,使用 1 个默认参数 Complex c; //不向构造函数传递实参,使用全部默认参数 a = 25.5; //调用转换构造函数(向构造函数传递 1 个实参,使用 1 个默认参数) return 0; }精简后的构造函数包含了两个默认参数,在调用它时可以省略部分或者全部实参,也就是可以向它传递 0 个、1 个、2 个实参。转换构造函数就是包含了一个参数的构造函数,恰好能够和其他两个普通的构造函数“融合”在一起。
本文链接:http://task.lmcjl.com/news/8758.html