Chinaunix首页 | 论坛 | 博客
  • 博客访问: 201631
  • 博文数量: 26
  • 博客积分: 567
  • 博客等级: 中士
  • 技术积分: 420
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-05 18:48
文章分类

全部博文(26)

文章存档

2011年(26)

分类: C/C++

2011-10-11 19:35:27

C++运算符重载

 

<<”和“>>”本来是在C++中被定义为左/右位移运算符的,由于在iostream头文件中对它们进行了重载,所以使用它们能用作标准数据类型数据的输入和输出运算符。因此,在使用它们的程序中必须包含:#include

 

1. 运算符重载例子:

1重载函数作为Complex类的成员函数

class Complex

{

public:

       Complex operator +(Complex &c2);    //声明重载运算符’+’的函数

       ……

};

Complex Complex::operator + (Complex &c2)       //定义该函数

{ return Complex(real+c2.real, imag+c2.image); }

int main()

{

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

       c3 = c1 + c2;      //调用

       ……

}

 

分析:C++编译系统将程序中的表达式c1 + c2解释为:

       c1.operator +(c2)

即以c2为实参调用对象c1的运算符重载函数operator +(Complex &c2)。实际上,运算符重载函数有两个参数,由于重载函数是Coplex类中的成员函数,有一个参数是隐含的,运算符函数是用this指针隐式地访问类对象的成员,如this->real+c2.realthis代表c1,即实际上是c1.real+c2.real

 

运算符重载函数可以是类的成员函数,也可以是类的友元函数,也可以是普通函数(不推荐)。

 

2 重载函数作为Complex类的友元函数

friend Complex operator +(Complex &c1, Complex &c2);  //声明

 

Complex operator +(Complex &c1, Complex &c2) //定义

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

c3 = c1+c2; //调用

 

为什么把运算符函数作为友元函数呢?理由很简单,因为运算符函数要访问Complex类对象中的成员。如果作为成员函数,就必须要求运算表达式第一个参数(即运算符左侧的操作数)是一个类对象,而且与运算符函数的类型相同,在例1中,表达式c1+c2中的第一个参数c1Complex类对象,运算符函数返回值的类型也是Complex,这是正确的。如果c1不是Complex类,就无法启用c1.operator +(c2),也就无法通过隐式this指针访问Complex类的成员了。

说明:有的C++编译系统(如VC++6.0)没有完全实现C++标准,它所提供的后缀.h的头文件不支持把成员函数重载为友元函数,因此在VC++6.0,应把程序的头两行:

#include

using namespace std;

改为一行:#include   //C语言的风格

 

3 重载双目运算符

bool operator >(String &string1, String &string2)

{

       if( strcmp(string1.p, string2.p) > 0 )  return true;

       else        return false;

}

 

4 重载单目运算符

Time Time::operator ++()       //定义前置自增运算符“++”重载函数

{

       if( ++sec >= 60)  //隐含了this指针

       { sec -=60;

         ++minute; }

       return *this;       //返回当前新的this指针内容

}

Time Time::operator ++(int) //定义后置“++”重载函数

{

       Time temp(*this);      //定义新对象temp,将当前this指针指向的对象拷贝给temp

       sec++;

       if( sec >= 60)

       { sec -=60;

         ++minute; }

       return temp;      //返回未更新值的对象temp,实际上实参的值已经自增了。

}

说明:重载后置自增运算符时,多了一个int型的参数,增加这个参数只是为了与前置自增运算符重载函数有所区别,此外没有任何作用。

 

5 重载流插入/提取运算符

 

对“<<”和“>>”重载的函数形式如下:

istream & operator >>(istream &, 自定义类 &);      //input

ostream & operator >>(ostream &, 自定义类 &);    //output

例如:

ostream & operator <<(ostream &output, Complex &c) //定义流提取运算符“<<”重载函数

{

       output<<”(”<

       return output;

}

 

cout<    //调用重载后的"<<",等价于(operator <<(cout, c3))<

 

编译系统把”cout<解释为:operator <<(cout, c3),即以coutc3作为实参,调用operator <<(ostream &output, Complex &c)函数。因此,相当于执行了:

cout<<”(”<

返回的是流提取对象cout,作为引用返回。

 

如果有以下输出:

cout<

先处理cout<,即(cout<cout<其实是operator <<(cout, c3),返回的是流提取对象cout,所以cout再和后面的c2结合,输出c2的内容。可见为什么C++规定“流提取运算符重载函数的第一个参数和函数的类型都必须是ostream类型的引用”了,就是为了返回cout,以便连续输出。

 

问:为什么要在operator前面加’&’?(即“返回引用”)

答:开始我以为ostream & operator >>()的这个返回类型“ostream &”是为了和括号中的参数“ostream &”类型相对于,其实不然。因为只有运算符重载函数为成员函数时,才要求第一个参数和运算符重载函数的类型相同,而友元函数没有这个要求。

       先分析“ostream &”作为参数的情况,传入的第一个参数显然是coutcout是什么,它其实也是一个类的对象,“cincout分别是istream类和ostream类的对象”,所以说形参定义成引用,避免了对象的拷贝。

       再来分析函数类型为什么要定义为“ostream &”,先看看原理http://blog.csdn.net/zollty/article/details/6695311。因为要使返回的对象cout能够直接使用,所以要将函数的返回值定义为引用。

 

 

2. 不同数据类型之间的转换

a. 标准类型数据之间的转换

显示类型转换:

C语言格式:(类型名)数据,例如:(int)89.5

C++格式:类型名(数据),例如:int(89.5)

 

b. 用构造函数进行类型转换

几种构造函数:

l  默认构造函数(无参,直接指定默认值):Complex();

l  用于初始化的构造函数:Complex(double r, double i);

l  用于复制对象的复制构造函数:Complex(Complex &c);

l  现在要讲解的新构造函数——转换构造函数Complex(double r){ real=r; imag=0; }(它只有一个参数)

以上几种构造函数可以同时出现在同一个类中,它们是构造函数的重载。

 

假如有以下声明语句:

Complex c1(3.5);      //建立对象c1,由于只有一个参数,调用转换构造函数

也可以用声明语句建立一个无名的Complex类对象。如:

Complex(3.6);    //用声明语句建立一个无名对象,合法,但是无法使用

可以在一个表达式中使用无名对象,如

c1=Complex(3.6);     //假设c1已经被定义为Complex类对象

若在程序中有以下表达式:

c=c1+2.5;

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

c=c1+Complex(2.5);

请对比Complex(2.5)int(2.5)。可以认为Complex(2.5)的作用也是强制类型转换。通常把有一个参数的构造函数用作类型转换,所以,称为转换构造函数。如可以将一个学生类对象转换为教师类对象,可以在Teacher类中写出下面的转换构造函数:

Teacher(Student &s)

{ num=s.num; strcpy(name, s.name); ***=s.***; }

但应注意:对象s中的num, name, ***必须是公用成员,否则不能被类外调用。

 

c. 用类型转换函数进行类型转换

       用转换构造函数可以将一个指定类型的数据转换为类的对象。但是不能反过来将一个类的对象转换为一个其他类型的数据(例如将一个Complex类对象转换成double类型数据)。C++提供了类型转换函数来解决这个问题。类型转换函数的作用是将一个类的对象转换成另一类型的数据。如果已经声明了一个Complex类:

class Complex

{

public:

       ……

private:

       double real;

       double imag;

};

可以在Complex类中这样定义类型转换函数:

operator double()

{ return real; }

       它的作用是将一个Complex类对象转换为一个double型数据,其值是Complex类中得数据成员real的值。请注意:函数名是operator double。这点是和运算符重载时的规律一致的(都是用关键字operator开头,只是被重载的是类型名)。其特点:在函数名前面不能指定函数类型,函数也没有参数。其返回值的类型是由函数名中指定的类型名来确定的(函数返回double型变量real的值)。

       在定义了前面的数据类型转换函数后,程序中的Complex类对象是不是一律都转换成double类型的数据呢?不是的,它们具有双重身份,既是Complex类对象,又可以作为double类型数据。

       转换构造函数和类型转换运算符有一个共同的功能:当需要的时候,编译系统会自动的调用这些函数,建立一个无名的临时对象(或临时变量)。例如,若已定义d1, d2double型变量,c1, c2Complex类对象,且类中已经定义了类型转换函数。设程序中有以下表达式:

       d1 = d2 + c1;

编译系统发现“+”左侧的d2double型,而右侧的c1Complex类对象,如果没有对运算符“+”进行重载,就会检查有无类型转换函数,结果发现有,就调用operate double函数把Complex类对象c1转换为double型数据,建立了一个临时的double变量,并与d2相加,最后将一个double的值赋给d1。相当于执行表达式:

       d1 = d2 + c1.operate double();

如果类中已经定义了转换构造函数并重载了运算符“+”(作为Complex类的友元函数),但未对double定义类型转换函数(或者说未对double重载),若有以下表达式:

       c2 = c1 + d2;

则系统将转换为:

       c2 = c1+ Complex(d2);

当然,如果既有转换构造函数和运算符重载,又有double类型转换函数,出现歧义的话就会出错。

 

阅读(8583) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~