技术交流QQ群:1027579432,欢迎你的加入!
1.C++转换构造函数:将其它类型转换为当前类的类型
- 在C/C++中,不同的数据类型之间可以相互转换。无需用户指明如何转换的称为自动类型转换(隐式类型转换),需要用户显式地指明如何转换的称为强制类型转换。
// 自动类型转换示例 int a = 6; a = a + 3.12; // 将小数转换为整数会抹掉小数点后面的数字 // 强制类型转换示例 int b = 100; int *p = &b; float *ptr = (float *)p; // 将int *转换为float *只是简单地复制指针的值
-
不管是自动类型转换还是强制类型转换,前提必须是编译器知道如何转换,例如,将小数转换为整数会抹掉小数点后面的数字,将int *转换为float *只是简单地复制指针的值,这些规则都是编译器内置的,我们并没有告诉编译器。总之,如果编译器不知道转换规则就不能转换,使用强制类型也无用,请看下面的例子:
#include "iostream" using namespace std; // 如果编译器不知道转换规则就不能转换,使用强制类型也无用 class complex{ public: complex():m_real(0.0), m_img(0.0){} complex(double real, double img):m_real(real), m_img(img){} public: friend ostream & operator<< (ostream &out, const complex &c); // 友元函数 private: double m_real; double m_img; }; ostream & operator << (ostream &out, const complex &c){ out << c.m_real << "+" << c.m_img << "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。但是,编译器并不知道这个转换规则,这超出了编译器的处理能力,所以转换失败,即使加上强制类型转换也无用。幸运的是,C++允许我们自定义类型转换规则,用户可以将其它类型转换为当前类类型,也可以将当前类类型转换为其它类型。这种自定义的类型转换规则只能以类的成员函数的形式出现。换句话说,这种转换规则只适用于类,本节先讲解如何将其它类型转换为当前类类型。
2.转换构造函数
-
将其它类型转换为当前类类型需要借助转换构造函数(Conversion constructor)。转换构造函数也是一种构造函数,它遵循构造函数的一般规则,转换构造函数只有一个参数。
#include "iostream" using namespace std; /*---------------------------将其它类型转换为当前类的类型-------------------------------*/ // 如果编译器不知道转换规则就不能转换,使用强制类型也无用 class complex{ public: complex():m_real(0.0), m_img(0.0){} complex(double real, double img):m_real(real), m_img(img){} complex(double real): m_real(real), m_img(0.0){} // 转换构造函数,作用是将double类型的参数real转换成 complex类的对象,并将real作为复数的实部,将0作为复数的虚部 public: friend ostream & operator<< (ostream &out, const complex &c); // 友元函数 private: double m_real; double m_img; }; ostream & operator << (ostream &out, const complex &c){ out << c.m_real << " + " << c.m_img << "i"; return out; } int main(){ complex a(10.0, 20.0); cout << "a = " << a << endl; a = 25.5; // 将赋值的过程转换成了函数调用的过程 cout << "a = " << a << endl; return 0; }
- 转换构造函数也是构造函数的一种,它除了可以用来将其它类型转换为当前类类型,还可以用来初始化对象,这是构造函数本来的意义。下面创建对象的方式是正确的:
complex c1(23.1); // 创建具名对象 complex c2 = 24.3; // 以拷贝的方式初始化对象,编译器先调用转换构造函数,将24.3转换为complex类型(创建一个complex类的匿名对象),然后再拷贝给c2。 complex(3.21); // 创建匿名对象 c1 = complex(34.2); // 创建一个匿名对象并将它赋值给 c1
- 如果已经对+运算符进行了重载,使之能进行两个complex类对象的相加,那么下面的语句也是正确的:
complex c1(15.6, 89.9); complex c2; c2 = c1 + 29.6; cout << c2 << endl;
- 注意:为了获得目标类型,编译器会“不择手段”,会综合使用内置的转换规则和用户自定义的转换规则,并且会进行多级类型转换,例如:
- 编译器会根据内置规则先将int转换为double,再根据用户自定义规则将double转换为complex(int --> double --> complex);
- 编译器会根据内置规则先将char转换为int,再将int转换为double,最后根据用户自定义规则将double转换为complex(char --> int --> double --> 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; }
3.再谈构造函数
- 构造函数的本意是在创建对象的时候初始化对象,编译器会根据传递的实参来匹配不同的(重载的)构造函数,几种形式的构造函数:
- 默认构造函数: 就是编译器自动生成的构造函数,以complex类为例,它的原型为:
complex(); // 没参数
- 普通构造函数: 就是用户自定义的构造函数,以complex类为例,它的原型为:
complex(double real, double img); // 两个参数
- 拷贝构造函数: 在以拷贝的方式初始化对象时调用,以complex类为例,它的原型为:
complex(const complex &c);
- 转换构造函数: 将其它类型转换为当前类类型时调用,以complex为例,它的原型为:
complex(double real); // 一个参数
- 默认构造函数: 就是编译器自动生成的构造函数,以complex类为例,它的原型为:
- 不管哪一种构造函数,都能够用来初始化对象,这是构造函数的本意。假设complex类定义了以上所有的构造函数,那么下面创建对象的方式都是正确的:
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)
- 除了在创建对象时初始化对象,其他情况下也会调用构造函数,例如:以拷贝的的方式初始化对象时会调用拷贝构造函数,将其它类型转换为当前类类型时会调用转换构造函数。这些在其他情况下调用的构造函数,就成了特殊的构造函数了。特殊的构造函数并不一定能体现出构造函数的本意。
4.对complex类的进一步精简
- 上面的complex类中我们定义了三个构造函数,其中包括两个普通的构造函数和一个转换构造函数。其实,借助函数的默认参数,我们可以将这三个构造函数简化为一个,请看下面的代码:
#include "iostream" using namespace std; /*---------------------------将其它类型转换为当前类的类型-------------------------------*/ // 如果编译器不知道转换规则就不能转换,使用强制类型也无用 class complex{ public: complex(double real=0.0, double img=0.0):m_real(real), m_img(img){} public: friend ostream & operator<< (ostream &out, const complex &c); // 友元函数 private: double m_real; double m_img; }; ostream & operator << (ostream &out, const complex &c){ out << c.m_real << " + " << c.m_img << "i"; return out; } int main(){ complex a(10.0, 20.0); // 向构造函数传递2个实参,不使用默认参数 cout << "a = " << a << endl; complex b(89.5); // 向构造函数传递1个实参,使用1个默认参数 cout << "b = " << b << endl; complex c; // 不向构造函数传递实参,使用全部默认参数 a = 25.5; // 调用转换构造函数(向构造函数传递1个实参,使用1个默认参数) cout << "a = " << a << endl; return 0; }