c++ tricks——类的默认类型转换

转换构造函数

转换构造函数的作用是将某种类型的数据转换为类的对象,当一个构造函数只有一个参数,而且该参数又不是本类的const引用时,这种构造函数称为转换构造函数。例子:

class A {

public:

    int a;

    A(int a) :a(a) {}

    A reta() {

        return a;

    }

};

int main() {

    A a(2);

    A b = a.reta();

    A c = 3;

    cout<<b.a<<"\n"<<c.a<<endl;

    return 0;

}

// 结果是输出23

实际上这是由隐式转换机制造成的,如果不想要这种效果,可以在构造函数前加上explicit声明。加上之后上面的代码就会编译出错,提示:

无法从”int”转换为”A”

类型转换运算符重载

用转换构造函数可以将一个指定类型的数据转换为类的对象。但是不能反过来将一个类的对象转换为一个其他类型的数据(例如将一个Complex类对象转换成double类型数据)。在C++提供默认类型转换函数(type conversion function)来解决这个问题。类型转换函数的作用是将一个类的对象转换成另一类型的数据

如果已声明了一个Complex类,可以在Complex类中这样定义类型转换函数:

operator double( ) {

         return real;

}

函数返回double型变量real的值。它的作用是将一个Complex类对象转换为一个double型数据,其值是Complex类中的数据成员real的值。请注意,函数名是operatordouble,这点是和运算符重载时的规律一致的(在定义运算符“+”的重载函数时,函数名是operator +)。

类型转换函数的一般形式为:

operator 类型名( ) {

         实现转换的语句

}

在函数名前面不能指定函数类型,函数没有参数其返回值的类型是由函数名中指定的类型名来确定的。类型转换函数只能作为成员函数,因为转换的主体是本类的对象。不能作为友元函数或普通函数。

从函数形式可以看到,它与运算符重载函数相似,都是用关键字operator开头,只是被重载的是类型名。double类型经过重载后,除了原有的含义外,还获得新的含义(将一个Complex类对象转换为double类型数据,并指定了转换方法)。这样,编译系统不仅能识别原有的double型数据,而且还会把Complex类对象作为double型数据处理。

那么程序中的Complex类对具有双重身份,既是Complex类对象,又可作为double类型数据。Complex类对象只有在需要时才进行转换,要根据表达式的上下文来决定。

转换构造函数和类型转换运算符有一个共同的功能:当需要的时候,编译系统会自动调用这些函数,建立一个无名的临时对象(或临时变量)

:这里是只有在需要的时候才会去调用。

例子:

#include <iostream> 

using namespace std; 

class Complex { 

public: 

   Complex(){real=0;imag=0;} 

   Complex(double r, double i){real=r; imag=i;} 

   operator double() {return real;} //类型转换函数 

private: 

   double real; 

   double imag; 

}; 

int main( ) { 

   Complex c1(3,4),c2(5,-10),c3; 

   double d; 

   d=2.5+c1;//要求将一个double数据与Complex类数据相加 

   cout<<d<<endl; 

   return 0; 

}

//输出结果:5.5

对程序的分析:

1) 如果在Complex类中没有定义类型转换函数operatordouble,程序编译将出错。

因为不能实现double 型数据与Complex类对象的相加。现在,已定义了成员函数 operator double,就可以利用它将Complex类对象转换为double型数据。请注意,程序中不必显式地调用类型转换函数,它是自动被调用的,即隐式调用

在什么情况下调用类型转换函数呢?

编译系统在处理表达式 2.5 +c1时,发现运算符“+”的左侧是double型数据,而右侧是Complex类对象,又无运算符“+”重载函数,不能直接相加,编译系统发现有对double的重载函数,因此调用这个函数,返回一个double型数据,然后与2.5相加。

2) 如果在main函数中加一个语句:c3=c2;请问此时编译系统是把c2按Complex类对象处理呢,还是按double型数据处理?

由于赋值号两侧都是同一类的数据,是可以合法进行赋值的,不会把c2转换为double型数据

3) 如果在Complex类中声明了重载运算符“+”函数作为友元函数

Complex operator+ (Complex c1,Complex c2) { //定义运算符“+”重载函数

         return Complex(c1.real+c2.real, c1.imag+c2.imag);

}

若在main函数中有语句

c3=c1+c2;

由于已对运算符“+”重载,使之能用于两个Complex类对象的相加,因此将c1和c2按Complex类对象处理,相加后赋值给同类对象c3。如果改为

d=c1+c2; //ddouble型变量

将c1与c2两个类对象相加,得到一个临时的Complex类对象,由于它不能赋值给double型变量,而又有对double的重载函数,于是调用隐式转换函数,把临时类对象转换为double数据,然后赋给变量double d。

 

从前面的介绍可知,对类型的重载和对运算符的重载的概念和方法都是相似的,重载函数都使用关键字operator。因此,通常把类型转换函数也称为类型转换运算符函数,由于它也是重载函数,因此也称为类型转换运算符重载函数(或称强制类型转换运算符重载函数)。

假如程序中需要对一个Complex类对象和一个double型变量进行+,-,*,/等算术运算,以及关系运算和逻辑运算,如果不用类型转换函数,就要对多种运算符进行重载,以便能进行各种运算。这样,是十分麻烦的,工作量较大,程序显得冗长。如果用类型转换函数对double进行重载(使Complex类对象转换为double型数据),就不必对各种运算符进行重载,因为Complex类对象可以被自动地转换为double型数据,而标准类型的数据的运算,是可以使用系统提供的各种运算符的。

 

例子

#include <iostream> 

using namespace std; 

class Complex { 

public: 

    Complex(){ real = 0; imag = 0; }  //默认构造函数 

    explicit Complex(double r){ real = r; imag = 0; }//转换构造函数 

    Complex(double r, double i){ real = r; imag = i; }//实现初始化的构造函数 

    friend Complex operator + (Complex c1, Complex c2); //重载运算符“+”的友元函数 

    void display(); 

private: 

    double real; 

    double imag; 

}; 

Complex operator + (Complex c1, Complex c2) { //定义运算符“+”重载函数 

    return Complex(c1.real + c2.real, c1.imag + c2.imag); 

void Complex::display() { 

    cout << "(" << real << "," << imag << "i)" << endl; 

int main() { 

    Complex c1(3, 4), c2(5, -10), c3; 

    c3 = c1 + 2.5; //复数与double数据相加 

    c3.display(); 

    return 0; 

}

// 输出的结果为:(5.5,4i)

对程序的分析:

1) 如果没有定义转换构造函数,则此程序编译出错。

2) 现在,在类Complex中定义了转换构造函数,并具体规定了怎样构成一个复数。由于已重载了算符“+”,在处理表达式c1+2.5时,编译系统把它解释为
    operator+(c1, 2.5)
由于2.5不是Complex类对象,系统先调用转换构造函数Complex(2.5),建立一个临时的Complex类对象,其值为(2.5+0i)。上面的函数调用相当于
    operator+(c1, Complex(2.5))
将c1与(2.5+0i) 相加,赋给c3。运行结果为(5.5+4i)

3) 如果把“c3=c1+2.5;”改为c3=2.5+c1; 程序可以通过编译和正常运行。过程与前相同。若Complex operator + (Complex c1, Complex c2);前加上explicit关键则,则编译报错。

结论

Ø  在已定义了相应的转换构造函数情况下,将运算符“+”函数重载为友元函数,在进行两个复数相加时,可以用交换律。

Ø  如果运算符函数重载为成员函数,它的第一个参数必须是本类的对象。当第一个操作数不是类对象时,不能将运算符函数重载为成员函数。如果将运算符“+”函数重载为类的成员函数,交换律不适用。由于这个原因,一般情况下将双目运算符函数重载为友元函数。单目运算符则多重载为成员函数。

4) 如果一定要将运算符函数重载为成员函数,而第一个操作数又不是类对象时,只有一个办法能够解决,再重载一个运算符“+”函数,其第一个参数为double型。当然此函数只能是友元函数,函数原型为
    friend operator+(double, Complex&);
显然这样做不太方便,还是将双目运算符函数重载为友元函数方便些。

5) 若在上面程序的基础上增加类型转换函数
    operator double( ){return real;}
此时Complex类的公用部分为:

public:

   Complex( ){real=0;imag=0;}

   Complex(double r){real=r;imag=0;}  //转换构造函数

   Complex(double r,double i){real=r;imag=i;}

   explicit operator double( ){return real;}//类型转换函数

   friend Complex operator+ (Complex c1,Complex c2); //重载运算符“+

   void display( );

c3 = c1 + 2.5; //编译出错,编译器不知调用Complex(2.5)还是operator double( )?

其余部分不变。程序在编译时出错,原因是出现二义性。当类中同时出现了转换构造函数和类型转换函数的时候,必须要注意是否会出现二义性的问题。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值