C++ 面向对象- -运算符的重载(三)

目录

不同类型数据间的转换

1、标准类型数据间的转换

2、转换构造函数

3、类型转换函数

关于运算符重载的归纳


 

不同类型数据间的转换

1、标准类型数据间的转换

在以前我们对一个变量初始化或者不同数据间进行运算时,都会涉及到数据类型的转换,只是有显式和隐式的区别。隐式类型转换是编译系统自动完成的,不需要用户干预,例:

int i=6;
i=7.5+i;

编译系统对 7.5 是作为 double 型数处理的,在求解表达式时,先将 i 的值 6 转换成 double 型,然后与 7.5 相加,得到和为 13.5 ,在向整型变量 i 赋值时,将 13.5 转换成整数 13 ,然后赋给 i 。

显式类型转换是人为地指定将一种指定的数据转换成另一指定的类型,其形式为: 类型名(数据),或者 (类型名)数据,但提倡用后边那种形式,看着比较清晰。

 

2、转换构造函数

转换构造函数的作用是将一个其类型的数据转换成一个类的对象。在此之前回顾一下之前学的构造函数,以现有一个复数类为例,默认构造函数(Complex(),无形参)、用于初始化的构造函数(Complex(double r,double i),为类对象进行初始化)、复制构造函数(Complex(Complex c),形参是本类对象)。

转换构造函数只有一个形参,如 

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

作用就是将 double 型参数 r 转换成 Complex 类的对象,将 r 作为复数的实部,虚部为0(这部分可以根据需要定义转换构造函数)。在一般的类体中,可以有转换构造函数,也可以没有转换构造函数,视需要而定。

举一些例子来说:如果在Complex 类中定义了上面的转换构造函数,那么在 Complex 类的作用域内可定义 Complex c1(3) ,由于其只有一个参数,故会调用上面的转换构造函数,将 int 型常数转换成一个名为 c1 的对象,其实部为 3 ,虚部为 0;也可以建立一个无名对象,即Complex (3),虽然是合法的,但无法使用它,但可以有 c2 = Complex (3),其作用是建立一个无名对象,值为(3+0i),然后将此无名对象的值赋给 c2 ,c2的值此时就是(3+0i);又如果已对运算符 “+”,进行了重载,使之能进行两个 Complex 类对象进行相加,若有表达式:c3=c1+2 ,编译是出错的,应先将 int 型数转换成类对象后再相加,即 c3=c1+Complex(2)。转换构造函数的作用类似于前边的强制类型转换,可以认为 Complex(2)也是作一个强制类型转换,是将 2 转换成 Complex 类对象。

转换构造函数也是构造函数的一种,遵循构造函数的一般规则,它的函数体中也可以不做类型转换功能,比如 Complex (double r){cout<< r ; },但这种用法没有任何意义,虽然转换构造函数是根据用户需要而确定,但最好使它有一定的实际意义。需要注意的是,转换构造函数只能有一个参数,如果参数多了的话,那么到底是将哪个参数转换成类的对象?另外转换构造函数不仅能把一个标准类型数据转换成类对象,也能够把另一个类的对象转换成构造函数所在的类对象,比如一个学生毕业后当了老师,可以将学生类对象转换成教师类对象,就是把学生的编号、姓名、性别复制到一个教师类对象中:

Teacher (Student s){
    num=s.num;
    sex=s.sex;
    strcpy(name,s.name);
}// 学生类中的这几个数据成员需要是 public 的,不然不能再类外访问。

3、类型转换函数

该函数与上边的转换构造函数功能恰恰相反,转换构造函数是为了将一个标准类型的数据(如 int 型)转换成一个类对象,而类型转换函数则是将一个类对象转换成一个标准类型的数据。其一般形式是 : operator 类型名(){实现转换的语句};注意类型转换函数的函数名是 operator 类型名 ,这是一部分,同为其函数名,别搞混淆了。那我们会发现类型转换函数的函数名前没有函数类型,也没有参数,其返回值的类型是由函数名中指定的类型名来确定的,如:

operator int (){
    return real ;
}

函数返回 int 类型的real值,它的作用是将一个 Complex 类对象转换成一个 int 型数据,其值是类中数据成员 real 的值,该函数名是 operator int ,有点类似运算符重载函数(operator+)。因为类型转换函数转换的主体是一个类对象,且没有形参,故它只能作为该类的成员函数,不能作为友元函数或者一般函数,那么有个问题是,Complex类对象是不是就一律转换成 int 类型数据呢?C++中规定,它们是具有“双重身份”,既是 Complex 类对象,又可作为 int 类型数据,相当于一个人有两种国籍,在不同的场合下用不同的面貌。

举一些例子来说明这些构造函数间复杂的使用关系:若已定义 i1 ,i2 作为 int 型变量, c1 , c2 作为 Complex 类对象,并且类中已定义了类型转换函数,有表达式 : i1=i2+c1 ,在编译时系统发现 + 左侧的 i2 是 int 型,右侧的 c1 是 Complex 类,如果没有对运算符 “+” 进行重载,就会检查有无类型转换函数,结果发现有对 int 型的重载函数,系统就会调用该函数,把 Complex 类对象 c1 转换成 int 型数据,建立一个临时的 int 型数据,并与 i2 相加,把最后的结果值赋给 i1 ;如果类中已经定义了转换构造函数并且又重载了运算符 “+”,但没有对 int 型定义类型转换函数(或者说没有对 int 重载),若有表达式: c1=c1+i1 ,在编译时系统发现 + 左侧的 c1 是Complex 类,右侧的 i1 是int 型,编译系统就会找有无对 + 的重载,发现有 operator + 函数,用于将两个复数类对象进行相加,但现在 i1 是 int 型 ,不合要求,在类中有没有对 int 进行重载,因此不可能把 c1 转换成 int 型数据再相加,那么编译系统找有无转换构造函数,发现有,就调用该函数去建立一个临时对象Complex(i1),再调用 operator+函数,将两个复数类相加,最后赋给 c1 。

通常把类型转换函数称为类型转换运算符函数,由于它也是重载函数,因此也称为类型转换运算符重载函数(或强制类型转换运算符重载函数)。

下面是一个例子,涉及几个函数的重载:

#include <iostream>
using namespace std;
class Complex{
		double real;
		double imag;
	public:
		Complex(double r=0,double i=0):real(r),imag(i){}
		friend Complex operator+(Complex ,Complex );
//		operator int (){
//			return real;
//		}
		Complex(int i){
			real=i;
			imag=0;
		} 
		friend ostream& operator<<(ostream& , Complex&);
};
Complex operator+(Complex c1,Complex c2){
	return Complex(c1.real+c2.real,c1.imag+c2.imag);
}
ostream& operator<<(ostream& output , Complex& c){
	output<<"("<<c.real << ","<<c.imag<<"i)"<<endl;
	return output;
}
int main(){
	Complex c1(1,1);
	Complex c2(2,2);
	Complex c3;
	int i=1 ;
	int j=2 ;
//	j=i+c1;
	cout<<"j="<<j<<endl;
	c3=c1;
//	j=c2+c3;
	cout<<"j="<<j<<endl;
	c3=j+c2;
	cout<<c3;
} 

关于类型转换函数、转换构造函数、运算符的重载等函数,如果一起使用的话注意不要出现二义性,我上边的例子中就有这种情况,大家有兴趣可以自己试一下,填入一些数据看看结果是不是自己所想要的,以及错的话该怎么改。还记得前边在说运算符+重载函数时,要将一个标准类型数据和一个类对象相加,如果两者的位置不一样,作为友员重载函数只需将形参对应互换一下位置,或者再重载一次,而如果作为成员重载函数就会出错,此处运用转换构造函数和运算符+重载函数就能够实现自由相加,但如果运算符重载函数是作为成员函数的话,交换律是不适用的,作为成员函数需要其第一个操作数必须是类对象,也正是由于这个原因,一般情况下将双目运算符重载为友元函数,单目运算符则多重载为成员函数,有兴趣也可以都试一下。

总之这些运算符的重载和构造函数都是根据问题需要进行设置,一般情况下用到的地方不多,但还是很重要的知识点。

关于运算符重载的归纳

运算符的重载使类的设计更加丰富多彩,扩大了类的功能和适用范围,使程序易于理解,也易于对对象进行操作,体现了为用户着想、方便用户使用的思想。有了运算符的重载,在声明了类之后,我们就可以把用于标准类型的1运算符用于自己声明的类,类的声明是一劳永逸的,有了一个好的类后,在程序中就不必定义许多成员函数去完成某些运算和输入输出的功能,使主函数更加简单易读,好的运算符重载能体现面向对象程序设计思想。

但在设计运算符重载时要注意:1、要先确定重载的是哪一个运算符,以及要将它作用于哪个类,重载运算符只能用于一个指定的类。2、设计运算符重载函数的功能完全是由设计者指定,最终的目的是实现用户对使用运算符的要求。3、在实际工作中,这些运算符的重载都是已经被设计好的,统一放在一个头文件中(文件名自定)。4、我们需要对运算符的重载能够看懂,知道具体实现什么功能。5、如果有需要,并无现成的重载运算符可用,此时需要自己设计,最好把每次设计的运算符重载函数保留下来,以免以后会再用到。

最后说一点在重载运算符中使用引用的重要性(我好像都不习惯用引用。。。),引用是通过地址传递使形参称为实参的别名,没有生成临时变量,减少时间和空间的开销。此外,如果重载函数返回值是对象的引用时,返回的不是常量,而是引用所代表的对象,它可以出现在赋值号的左侧而成为左值,可以被赋值或参与其他操作(如 保留cout 流的当前值以便能连续用 << 输出 ),但使用引用时要小心,因为改变了引用就等于改变了它所代表的对象。

 


猜你喜欢

转载自blog.csdn.net/qq_43305922/article/details/85457293