运算符重载详解(五)

9.转换构造函数进行不同类型数据的转换
转换构造函数的作用是将一个其他类型的数据转换成一个类的对象。
转换构造函数也是一种构造函数,它遵循构造函数的一般规律,通常把有一个参数的构造函数用作类型转换,所以,称为转换构造函数。
注意:转换构造函数只能有一个参数,如果有多个参数的,它就不是转换构造函数,原因很简单:如果有多个参数的话,究竟是把那个参数转换成Complex类的对象呢?

使用转换构造函数将一个指定的数据转换为类对象的方法如下:
<1>先声明一个类
<2>在这个类中定义一个只有一个参数的构造函数,参数的类型是需要转换的类型,在函数体中指定转换的方法。
<3>在该类的作用域内可以用以下形式进行类型转换:
类名(指定类型的数据)
就可以将指定类型的数据转化为此类的对象。
不仅可以将一个标准类型数据转换成类对象,也可以将另一个类的对象转换成转换构造函数躲在的类对象。如将一个学生类对象转换为教师类对象。如
Teacher(Student& s){num =s.num;strcpy(name,s.name);}
但是,对象s中的num,name必须是公有成员,否则不能被类外引用。

以前学过的构造函数有:
默认构造函数:如Complex();//没有参数
用于初始化的构造函数:如Complex(double r, double i);
用于复制对象的复制构造函数:如Complex(Complex& c);//形参是本类对象的引用
转换构造函数只有一个参数,如:

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

其作用是将double型的参数r转换成Complex类的对象,将r作为复数的实部,虚部为0。
上述构造函数可以同时出现在同一个类中,他们都是构造函数的重载。编译系统会根据建立对象时给出的实参的个数与类型选择形参与之匹配的构造函数。
例子:

Complex c1(3.5); //将double型常数转换成一个名为c1的Complex类对象
Complex c1;  //建立无名的对象
c1 = Complex(3.5);

若已对运算符+进行了重载,使之能进行两个Complex类对象的相加,若有以下表达式

c3 = c1 + 2.5;   //编译出错

编译出错,不能用运算符+将一个Complex类对象和一个浮点数相加,可以先将2.5转换为Complex类无名对象,然后相加

c3 = c1 + Complex(2.5); //合法

10.类型转换函数
类型转化函数的作用是将一个类的对象转换成另一类型的数据。类型转换函数的一般形式:
operator 类型名()
{实现转换的语句}
注:在函数名前面不能指定函数类型,函数没有参数。返回值类型是由函数名中指定的类型名来确定的
类型转换函数只能作为成员函数,因为转换的主体是本类的对象,不能作为友元函数或普通函数。

例子:
如已声明了一个Complex类,定义类型转换函数如下:

operator double()  //operator double是函数名
{ return real;}

函数返回值是double型变量real的值,作用是将一个Complex类对象转换为一个double型数据,其值是Complex类中的数据成员real的值。
<1>已经定义d1,d2为double型变量,c1,c2为Complex类对象,类中已经定义了类型转换函数,如下表达式:

d1 = d2 + c1; //类型转换函数将c1转换为double

编译器发现“+”的左侧d2是double型,而右侧的c1是Complex类对象,如果没有对运算符“+”进行重载,就会检查有无类型转换函数,若有对double的重载函数,就把Complex类对象c1转换为double型数据,建立一个临时的double数据,并与d2相加。
<2>若类中已定义了转换构造函数并且又重载了运算符“+”(作为Complex类的友元函数),但未对double定义类型转换函数(或者说未对double重载),以下表达式

c2 = c1 + d2; //

该怎么处理?
运算符“+”左侧的c1是Complex类对象,右侧d2是double型,编译系统寻找有无对“+”的重载,发现有operator + 函数,但是它是Complex类的友元函数,要求两个Complex类的形参,而现在d2是double,不符合要求,类中有没有对double进行重载,因此不可能把c1转换为double然后相加,编译系统就去找有无转换构造函数,发现有,就调用转换构造函数Complex(d2),建立一个临时的Complex类对象,再调用operator + 函数,将两个复数相加,相当于表达式:

c2 = c1 + Complex(d2);

<3>若已对运算符“+”重载,使两个Complex类对象相加,则以下表达式

d1 = c1 + c2;

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

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; //调用operator double类型转换函数,将Complex类对象隐式转换为double型数据
	cout << d;  //5.5
	return 0;
}

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

例子:

class Complex
{
public:
	Complex(){ real = 0, imag = 0; } //定义构造函数
	Complex(double r, double i){ real = r, imag = i; }//构造函数重载,函数初始化
	Complex(double r){ real = r, imag = 0; }//转换构造函数
	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";}
int main()
{
	Complex c1(3, 4), c2(5, -10), c3;
	c3 = c1 + 2.5;//operator + (c1,Complex(2.5))
	c3.display();
	return 0;
}

c3 = c1 + 2.5;//operator + (c1,Complex(2.5))改为c3 = 2.5 + c1;//operator + (Complex(2.5), c1)也是可以的。
重要结论
在已定义了相应的转换构造函数情况下,将运算符“+”函数重载为友元函数,在进行两个复数相加时,可以用交换律。

问题:能否将运算符“+”重载函数作为Complex类的成员函数得到上述结果呢?
作为Complex类的成员函数是不行的。
成员函数原型:operator + (Complex c2);函数的第一个参数省略了,它隐指this所指的对象,如果表达式为:c1+2.5,编译系统解释为c1.operator + (Complex (2.5));如果表达式为:2.5+c1,编译系统解释为(2.5).operator+(c1)显然是错误的,无法实现的。

结论:如果运算符函数重载为成员函数,它的第1个参数必须是本类的对象,。当第1个操作数不是类对象时,不能将运算符函数重载为成员函数。如果将运算符“+”函数重载为类的成员函数,交换律不适应。
由于这个原因,一般情况下将双目运算符函数重载为友元函数。单目运算符则多重载为成员函数。
如果一定要将运算符函数重载为成员函数,而第1个操作数又不是类对象时,只有一个办法能够解决,再重载一个运算符“+”函数,其第一个参数为doule型,当然此函数只能是友元函数,函数原型为:friend Complex operator + (double, Complex);显然是不太方便,还是重载为友元函数方便些。

问题:若此时类中包含转换构造函数、运算符“+”重载函数、类型转换函数,请分析他们之间的关系,如何执行,有无矛盾?

Complex(){ real = 0, imag = 0; } //定义构造函数
Complex(double r, double i){ real = r, imag = i; }//构造函数重载,函数初始化
Complex(double r){ real = r, imag = 0; }//转换构造函数
operator double(){ return real; } //类型转换函数,无参数
friend Complex operator + (Complex c1, Complex c2); //类型转换函数,一个形参

下面表达式该如何执行

c3 = c1 + 2.5;

程序在编译时出错,原因是在处理c1 + 2.5时出现二义性。
一种理解为调用转换构造函数,把2.5变成Complex类对象,然后调用运算符“+”重载函数,与c1进行复数相加。
另一种理解是,调用类型转换函数,把c1转换为double型数,然后与2.5进行相加。
系统无法判定,这二者是矛盾的。
如果要使用类型转换函数,就应当删去运算符“+”重载函数。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章