2025年4月10日 星期四 乙巳(蛇)年 正月十一 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > VC/VC++

再谈转换构造函数和类型转换函数

时间:01-05来源:作者:点击数:563

转换构造函数和类型转换函数的作用是相反的:转换构造函数会将其它类型转换为当前类类型,类型转换函数会将当前类类型转换为其它类型。如果没有这两个函数,Complex 类和 int、double、bool 等基本类型的四则运算、逻辑运算都将变得非常复杂,要编写大量的运算符重载函数。

但是,如果一个类同时存在这两个函数,就有可能产生二义性。下面以 Complex 类为例来演示:

  • #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);
  • friend Complex operator+(const Complex &c1, const Complex &c2);
  • operator double() const { return m_real; } //类型转换函数
  • private:
  • double m_real; //实部
  • double m_imag; //虚部
  • };
  • //重载>>运算符
  • ostream & operator<<(ostream &out, Complex &c){
  • out << c.m_real <<" + "<< c.m_imag <<"i";;
  • return out;
  • }
  • //重载+运算符
  • Complex operator+(const Complex &c1, const Complex &c2){
  • Complex c;
  • c.m_real = c1.m_real + c2.m_real;
  • c.m_imag = c1.m_imag + c2.m_imag;
  • return c;
  • }
  • int main(){
  • Complex c1(24.6, 100);
  • double f = c1; //①正确,调用类型转换函数
  • c1 = 78.4; //②正确,调用转换构造函数
  • f = 12.5 + c1; //③错误,产生二义性
  • Complex c2 = c1 + 46.7; //④错误,产生二义性
  • return 0;
  • }

①和②是正确的,相信大家很容易理解。

对于③,进行加法运算时,有两种转换方案:

  • 第一种方案是先将 12.5 转换为 Complex 类型再运算,这样得到的结果也是 Complex 类型,再调用类型转换函数就可以赋值给 f 了。
  • 第二种方案是先将 c1 转换为 double 类型再运算,这样得到的结果也是 double 类型,可以直接赋值给 f。

很多读者会认为,既然=左边是 double 类型,很显然应该选择第二种方案,这样才符合“常理”。其实不然,编译器不会根据=左边的数据类型来选择转换方案,编译器只关注12.5 + c1这个表达式本身,站在这个角度考虑,上面的两种转换方案都可以,编译器不知道选择哪一种,所以会抛出二义性错误,让用户自己去解决。

当然,你也可以认为编译器不够智能,没有足够强大的上下文(周边环境)推导能力。反过来说,即使我们假设编译器会根据=左边的数据类型来选择解决方案,那仍然会存在二义性问题,下面就是一个例子:

  • Complex c1(24.6, 100);
  • cout<<c1 + 46.7<<endl;

该语句没有将c1 + 46.7的结果赋值给其他变量,而是直接输出,这种情况应该将 c1 转换成 double 类型呢,还是应该将 46.7 转换成 Complex 类型呢?很明显都可以,因为转换构造函数和类型转换函数是平级的,没有谁的优先级更高,所以该语句也会产生二义性错误。

解决二义性问题的办法也很简单粗暴,要么只使用转换构造函数,要么只使用类型转换函数。实践证明,用户对转换构造函数的需求往往更加强烈,这样能增加编码的灵活性,例如,可以将一个字符串字面量或者一个字符数组直接赋值给 string 类的对象,可以将一个 int、double、bool 等基本类型的数据直接赋值给 Complex 类的对象。

那么,如果我们想把当前类类型转换为其它类型怎么办呢?很简单,增加一个普通的成员函数即可,例如,string 类使用 c_str() 函数转换为 C 风格的字符串,complex 类使用 real() 和 imag() 函数来获取复数的实部和虚部。

complex 是 C++ 标准库中的复数类,c是小写的,使用时需要引入complex头文件。Complex 是我们为了教学而自定义的复数类,C是大写的,Complex 类尽量模拟 complex 类。

下面是重新编写的 Complex 类,该类只使用了转换构造函数,没有使用类型转换函数,取而代之的是 real() 和 imag() 两个普通成员函数。一个实用的 Complex 类能够进行四则运算和关系运算,需要重载 +、-、*、/、+=、-=、*=、/=、==、!= 这些运算符,不过作为教学演示,这里仅仅重载了 +、+=、==、!= 运算符,其它运算符的重载与此类似。

  • #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);
  • friend istream & operator>>(istream &in, Complex &c);
  • friend Complex operator+(const Complex &c1, const Complex &c2);
  • friend bool operator==(const Complex &c1, const Complex &c2);
  • friend bool operator!=(const Complex &c1, const Complex &c2);
  • //以成员函数的形式重载
  • Complex & operator+=(const Complex &c);
  • public: //成员函数
  • double real() const{ return m_real; }
  • double imag() const{ return m_imag; }
  • private:
  • double m_real; //实部
  • double m_imag; //虚部
  • };
  • //重载>>运算符
  • ostream & operator<<(ostream &out, Complex &c){
  • out << c.m_real <<" + "<< c.m_imag <<"i";;
  • return out;
  • }
  • //重载<<运算符
  • istream & operator>>(istream &in, Complex &c){
  • in >> c.m_real >> c.m_imag;
  • return in;
  • }
  • //重载+运算符
  • Complex operator+(const Complex &c1, const Complex &c2){
  • Complex c;
  • c.m_real = c1.m_real + c2.m_real;
  • c.m_imag = c1.m_imag + c2.m_imag;
  • return c;
  • }
  • //重载+=运算符
  • Complex & Complex::operator+=(const Complex &c){
  • this->m_real += c.m_real;
  • this->m_imag += c.m_imag;
  • return *this;
  • }
  • //重载==运算符
  • bool operator==(const Complex &c1, const Complex &c2){
  • if( c1.m_real == c2.m_real && c1.m_imag == c2.m_imag ){
  • return true;
  • }else{
  • return false;
  • }
  • }
  • //重载!=运算符
  • bool operator!=(const Complex &c1, const Complex &c2){
  • if( c1.m_real != c2.m_real || c1.m_imag != c2.m_imag ){
  • return true;
  • }else{
  • return false;
  • }
  • }
  • int main(){
  • Complex c1(12, 60);
  • cout<<"c1 = "<<c1<<endl;
  • //先调用转换构造函数将 22.8 转换为 Complex 类型,再调用重载过的 + 运算符
  • Complex c2 = c1 + 22.8;
  • cout<<"c2 = "<<c2<<endl;
  • //同上
  • Complex c3 = 8.3 + c1;
  • cout<<"c3 = "<<c3<<endl;
  • //先调用转换构造函数将 73 转换为 Complex 类型,再调用重载过的 += 运算符
  • Complex c4(4, 19);
  • c4 += 73;
  • cout<<"c4 = "<<c4<<endl;
  • //调用重载过的 += 运算符
  • Complex c5(14.6, 26.2);
  • c5 += c1;
  • cout<<"c5 = "<<c5<<endl;
  • //调用重载过的 == 运算符
  • if(c1 == c2){
  • cout<<"c1 == c2"<<endl;
  • }else{
  • cout<<"c1 != c2"<<endl;
  • }
  • //先调用转换构造函数将 77 转换为 Complex 类型,再调用重载过的 != 运算符
  • if(c4 != 77){
  • cout<<"c4 != 77"<<endl;
  • }else{
  • cout<<"c4 == 77"<<endl;
  • }
  • //将 Complex 转换为 double,没有调用类型转换函数,而是调用了 real() 这个普通的成员函数
  • double f = c5.real();
  • cout<<"f = "<<f<<endl;
  • return 0;
  • }

运行结果:

c1 = 12 + 60i
c2 = 34.8 + 60i
c3 = 20.3 + 60i
c4 = 77 + 19i
c5 = 26.6 + 86.2i
c1 != c2
c4 != 77
f = 26.6
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门