您当前的位置:首页 > 计算机 > 编程开发 > VC/VC++

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

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

转换构造函数和类型转换函数的作用是相反的:转换构造函数会将其它类型转换为当前类类型,类型转换函数会将当前类类型转换为其它类型。如果没有这两个函数,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
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门