2010-11-27运算符重载(一)
一般说来,c++内定义类型(例如,int,char,float等)的操作用运算符来表示,其调用形式是表达式。用户定义类型的操作则用函数来表示,对它采用显式调用。为了使用户定义的类型与内定义类型一致,也允许用户定义类型使用运算符来表示操作。这种一致性还表现在可以为用户定义类型提供初始化函数,赋值函数以及转换规则等。如果利用重载的运算符来增添新的类型,例如复数,集合等,这些用户定义的新类型可以象内部定义的类型一样来使用。对c++各种运算符的重新定义,既运算符的重载。
假设定义了一个复数类,它可以将两个复数相加,例如:
class comlpex{
private:
double epart; //复数的实部
double ipart; //复数的虚部
public :
complex()
{
rpart+ipart+0.0; //缺省构造函数
}
complex(double rp,double ip)
{
rpart=rp;
ipart=ip;
}
complex add(const complex&com)
{
complex temp;
temp.rpart=com.rpart+rpart;
temp.ipart=com.ipart+ipart;
return temp;
}
};
现在,就可以用它来进行两复数的加法运算了:
complex a(10,7),b(3,5);
complex c=a.add(b); //即a+b
这种加法运算采用函数调用的形式,并不直观。我们希望能和整数或浮点数一样,采用+表达式来实施运算,为此,需要重载+运算符函数。运算符函数的名字是operator关键字,再接着重载的运算符。他的返回值和参数与普遍函数相同。现在,可以重新定义复数的加法:
class complex
{
//....
public:
complex operator+(const complex&com)
{
complex temp(rpart+com.rpart,ipart+com.ipart);
return temp;
}
//...
};
运算符函数operator+被定义为公有的,以便程序的其他函数能使用它,在定义了函数之后,就可以象在内定义类型上一样,对复数对象用+表达式实施运算,例如:
complex 1(10,7),b(3,5),c;
c=a+b
c++编译器把表达式a+b解释为函数调用a.operator+(b).在调用它时,operator+成员函数首先创建一个临时的complex对象temp,然后把出现在加法表达式中的两个复数之和暂存其内,最后将这个临时对象返回.
重载运算符与重载一般函数的区别
区别主要是在参数的个数上,c++的运算符所能操作的操作数的个数的个数是规定好的。在重载二元运算符时只能指定两个参数,而重载一元运算符时只能指定一个参数。在重载函数时,可以指定任意的返回类型,甚至可以指定void类型作为返回类型,因为c++允许反调用函数而可以不使用函数的返回值。但c++的运算符是用在表达式中,一个运算符的运算结果要供别的运算符使用,因此任何运算符都指定有非void类型的返回类型。
类运算符与友员运算符的选取
一般而言,对于二元运算符,将它重载为一个友员运算符比重载为一个成员运算符要便于使用。作为一个友员函数,这个运算符不要求第一个参数一定为某个类的对象。一元运算符重载为一个成员函数最恰当,重载为友员也可以。但若将增1或减1运算符重载为友员运算符,则要使用引用参数。
注:赋值运算符不能重载为友员运算符.
实 例(1):
class X
{
public:
int i;
};
X operator+(const X& x1,const X& x2)
{
X temp;
temp.i=x1.i+x2.i;
}
由于操作符的重载,使以下的运算合法
X x1,x2,x3; x1=x2+x3;
(1)运算符成员函数只能定义运算符的含义,不能改变运算符的优先级和结合顺序.例如,不论按何种方式重载运算符,a+b*c始终是先乘后加;而a=b=c也要先做b=c,然后,再对a赋值.
(2)运算符重载时,不能改变其目数.例如,任何企图把%定义为单目运算符,或把!定义为双目运算符的做法都会出错.
(3)运算符函数即可在类中定义,也可以在类外定义.在类外定义时,它至少应有一个相应类的参数.
(4)对于双目运算符@,当它用作成员函数时,只带一个参数(另一个运算符有调用它的对象给出),此时aa@bb与aa.operator@(bb)等价,当他用作外部函数时,带有两个参数(分别说明两个运算数),此时aa @bb与aa.operator@(aa,bb)等价
(5)对于单目前缀运算符@,当它用作成员函数时,不必带参数(运算符就是调用它的对象),此时@aa与aa.operator@()等价,当它用作外部函数时,带有一个参数(分别说明两个运算数),此时@aa与operator@(aa)等价
(6)对于单目后缀运算符@,当它用作成员函数时,应该带一个int参数(此参数一般不会使用,仅仅是为了把它同单目前缀运算符区分开),此时aa@与aa.operator@(int)等价,当它用作外部函数时,他应该带有两个参数,第一个是相应类的参数,第二个是int参数(也是用它来区分前后缀运算符),此时@aa与operator@(aa,int)等价.
(7)无论是在类中定义的运算符成员函数,还是在类外定义的运算符函数,都可以进行重载。也就是说可以定义多个同名的运算符函数。但其参数类型应有差别,否则会产生二义性。
(8)用户可重载已有的运算符,但不能定义自己的运算符,因为这常常会带来二义性,假设自定义**,以便进行乘方运算,编译器将无法确定它是按左结合(象fortran中那样),还是按右结合(象algol中一样);也无法确定表达式a**b是被当作a*(*b)还是a**b.
(9)编译器预定义了=(赋值),&(取地址),(顺序)这三种运算符,不必重载就可以使用它们。如果要限制外部函数对它的使用,可以把它们声明为私有的.
实 例(2):
//******************************
//*** 用成员函数重载运算符 ***
//******************************
#include <iostream.h>
class Complex
{
public:
Complex()
{
real = 0;
imag = 0;
}
Complex(double r, double i)
{
real = r;
imag = i;
}
Complex operator + (Complex& c);
Complex operator - (Complex& c); //二元减
Complex operator - (); //一元减
void print();
private:
double real;
double imag;
};
Complex Complex::operator + (Complex& c)
{
double r = real + c.real;
double i = imag + c.imag;
return Complex(r, i);
}
Complex Complex::operator - (Complex& c)
{
double r = real - c.real;
double i = imag - c.imag;
return Complex(r, i);
}
Complex Complex::operator - ()
{
return Complex(-real, -imag);
}
void Complex::print()
{
cout << "(" << real << ", " << imag << ")" << endl;
}
int main()
{
Complex a(5, 4), b(1, 7);
Complex c(0, 0);
c = a + b;
c.print();
c = a - b;
c.print();
c = -b;
c.print();
return 0;
}
用成员函数重载运算符称为类运算符。这种重载运算符函数的语义可表示为<对象>.operator<一元运算符>()或<对象1>.operator<二元运算符>(对象2)。前者可解释为给当前对象发送一个进行一元运算的消息;后者可解释为给当 前对象发送一个与对象