C++ 运算符重载 一个运算符赋予新的含义

来源:互联网 发布:mac usb没反应 编辑:程序博客网 时间:2024/05/12 05:52

一、为什么要重载操作符

1.                 操作符重载就是把操作符比如+,-,*,/这些运算符赋于新的意义。

2.                 操作符重载的目的:C++有许多内置的数据类型,包括mt,char,double等,每一种类型都有许多运算符,例如加+, 减,乘,除等。当用户定义了类的对象时,两个对象之间是不能进行这些操作的,比如hyong类的对象a+b,这样 的语句如果没有重载+运算符就会出错。但C++允许用户把这些运算符添加到自已的类中以方便类对象之间的运算就 像内置类型的运算一样方便,比如对象a+b这样就很明白更容易懂,当然也可以在类中定义一个对象间相加的函数,比如a.add(b)调用函数add()以实现两个对象a和b相加,但是这条语句没有比a+b更容易让人理解。

3.                 怎样实现操作符重载:要实现操作符重载就要使用操作符重载函数,操作符重载函数用关见字operator实现,其形 式为:返回类型operator操作符(参数列表){}。操作符重载函数是一个函数,只不过这个函数的函数名为operator再加上后面要重载的操作符而已,比如要重载+号,则:hyong operator +(hyong m){}这就声明了一个返回类型为 hyong的操作符函数,其函数名为operator +

4.                 操作符重载函数作为类的成员和友元或者独立于类的区别:当操作符重载函数作为类的成员函数时,操作符重载函 数的参数会比作为友元或者独立于类的操作符重载函数少一个参数,因为操作符重载类成员函数把调用该函数的第一个类的对象作为函数的第一个参数,也就是隐含的this指针指向调用该函数的第一个对象,所以会少一个参数。

5.                 调用操作符重载函数的方式:

5.1               调用类中的操作符重载函数的方法:当调用类中定义的操作符重载函数时最左边的对象是调用操作符重载函数 的对象。比如在类hyong中重定义的+操作符hyong operator +(hyong m){},有类hyong的对象m和n则调用 操作符重载函数的方法有m+n和m.operator +(n),前一条语句会自动转换为后面这条语句,且m+n的表达式 中最左边的对象是调用操作符重载函数的对象,而最右边的那个将被作为参数传送。也就是说n+m会转换成 n.operator +(m)。要记住当调用类中定义的操作符重载函数时最左边的对象是调用操作符重载函数的对象。

5.2                调用友元或独立的操作符重载函数的方法:当调用类的友元操作符重载函数或独立的操作符函数时语句m+n 会转换为显示的调用方式,比如有友元或独立操作符重载函数hyong operator +(hyong a, hyong扮{}则当出现 m+n时会转换成语句operator+(m, n)表达式的第一个对象传给第一个参数,第二个对象传给第二个参数。

6.                 什么情况下需要把操作符函数作为类的友元或者类的成员:一般来说操作符重载函数一般不要求作为类的成员函数 或者是友元函数,一般情况下可以将操作符重载函数作为类的成员函数。但是有一种情况必须要求操作符函数作为 类的友元函数或者是独立的函数,就是一个内置类型和对象相加的情况。比如有语句m+1和1+m第一条可以在类中 定义操作符函数的形式为hyong operator +(int i){},语句m+1可以调用这个函数是正确的,但对于1+m就不能调用 这个函数了,因为类中的操作符重载函数是最左边的对象是调用该函数的对象,但1+m最左边的是一个内置整型类 型1,所以不会调用这条语句,对于这种语句就只能把操作符重载函数定义为独立的函数或类的友元函数即形如 hyong operator +(int i , hyong a){}这样1+m就会转换成operator +(1, m)这样就是正确的。当然如果这个操作符重载函 数需要访问类中的私有成员时,就应把该函数定义为类的友元函数,如果不需要访问类中的私有成员,则可以定义 为友元也可以定义为独立函数。

7.                 必须把操作符函数作为类成员函数的运算符有:(),[],->和任何赋值运算符,重载这些运算符时必须把操作符函数 声明为类的成员函数。

8.                 重载操作符的限制:

8.1并不是所有的操作符都能被重载。除了.,.*,::,? :,sizeof^,typeid这几个运算符不能被重载,其他运算符 都能被重载

8.2         重载不能改变该运算符用于内置类型时的函义,程序员不能改变运算符+用于两个int型时的含义。

8.3        运算符函数的参数至少有一个必须是类的对象或者类的对象的引用。这种规定可以防止程序员运用运算符改变内置类型的函义。

8.4重载不能改变运算符的优先级。

8.5重载不能改变运算符的结合律。

8.6重载不能改变运算符操作数的个数。比如+需要两个操作数,则重载的+也必须要有两个操作数。

9.                 返回类型问题,在某些情况下操作符函数必须返回一个对象类型作为返回值,比如有hyong的对象a,b,c则语句a=b+c 其中重载的+操作符就必须返回一个hyong类型的一个对象,以便赋给对象a,不然该语句就会出错。当然你可以在 语句中返回一个临时对象,也可以返回一个对象的引用,或者返回this指针,不过返回临时对象会浪费内存开销, 所以最好返回类对象的一个引用。

10.           参数传递问题,操作符函数可以按值传递也可以按引用传递,这根据操作符而定,比如对于+运算符既可以把对象按 值传递给操作符函数也可以按引用传递给操作符函数,而且+操作符不会改变原操作数的值所以应把传递类型声明 为const,比如hyong operator +(const hyong &a, const hyong&b){}。但对于要改变其自身值的操作符比如++运算符, 就必须传递引用,且不能把该引用声明为const类型,因为如果操作数按值传递的话,传递给操作数函数的将是一个 对象的副本两个副本是独立的,不能改变到原对象的值所以应按引用传递对象,比如hyong operator ++(hyong &a){}。

二:重载一元运算符++

1.                 注意++有前缀和后缀之别,前缀形式是变量先加1然后执行表达式,而后缀形式则是先执行表达式然后再使变量加1,所以在执行后缀的++运算符时应先返回对象的原始值然后才对对象加1。

2.                 默认的带有一个参数的++运算符函数是前缀—运算符,要重载后缀的f+运算符必须采用另一种方式实现

重载后缀的++运算符时应给函数多增加一个int参数,该rnt参数不会使用,应忽略他,该参数只是为了让编译器区 分++运算符的前缀和后缀形式之间的区别i比如重载后缴■+运算符的友元函数形式^hyong operator ++(hyong &a, int i){}后面的参数int i没有实际意义,应忽略他。

例:重载++运算符的例子

class A{public: int b; A(){b=0;} A(int i){b=i;}~A(){cout<<〃xi〃<〈〃\n〃;}const A & operator ++( ) { ++b; return *this;} }; //定义前缀形式的++运算符,函数的返回类型既可以返回引用也可以是返回值,但 返回引用不会增加内存开销。返回类型可以是任意的,比如可以是int型,也可以是void,即没有返回值,但这里的返回类型必须是类类型A, 因为在main函数中表达式用于赋值运算符的左边,且把结果赋给了一个类A的对象。返回const的原因是防止++++k这样的情况出现,有const 就不能再改变返回对象的值即不能再对++k作增量运算。const A & operator ++(A &j, int i) //定义独立的后缀形式的++运算符,这里必须把第一个参数声明为对对象的引用,因为++运算符会 改变原始对象的值,如果不是引用就不能改变原始对象的值,也就达不到++运算符的笑果。注意int i参数是不使用的,只是让编译器区分 是前缀还是后缀的++运算符。{A t(j);//因为后缀形式的++运算符是先执行表达式后进行增量运算,所以这里应生成一个临时对象以便返回对象的原始值++j.b;//注意,因为独立函数既不是类的友元又不是类的成员,所以这里没有this指针,也不能直接访问类的成员。return t;}//返回对象t,这里会生成一个临时对象。int main(){ A m,n,k;m=++k;//调用前缀形式的++类成员运算符函数,语句等价于m=k.operator ++();因为前缀的++是类的成员,所以只能用点运算符调用,形如operator ++(k)将是错语的。cout<<m.b<<k.b; //输出11,前缀++是先使变量加再执行表达式,即对象k的值先加,然后再赋给对象m n=k. operator ++ ();//显示调用前缀的++类成员运算符函数。同m=k++。cout<<n.b<<k.b; //输出;;。n=k++;//调用后缀形式的独立++运算符函数,该语句等价于n=operator ++(k,l),其中后面的是没有意义的参数,只是为了让编译器区别是前缀还是后缀cout<<n.b<<k.b; //输出23,注意,这里n的值没有增加,因为后缀++是先执行表达式后使变量加。n= operator ++(k,l): //显示调用后缀的++独立运算符函数,同n=k++。注意整形实参在这里没有实用价值,但必须得有,以便指明是调用 的后缀++形式。cout<<n.b<<k.b; } //输出34。

三:重载二元运算符+

要注意重载二元运算符时如果有形如1+m这种类型的表达式就必须把该操作符函数定义为非类的成员的形式因为类中的操作符重载函数是最左边的对象是调用该函数的对象

class A{public: int b; A(){b=0;}~A(){cout<<〃xi〃<〈〃\n〃;}explicit A(int i){b=i;}//这里需要explicit关见字以防止自动的隐式类型转换,不然语句const A ^operator +(const A &j)和friendconst A ^operator +(const A &j,const int i)将出错二义性问题。也就是第一个操作符函数可以用隐式类型转换用语句m+3来调用。 //const A & operator +(const A &j){b=b+j.b;return *this;}//对于+操作符不会改变被加的操作数的值,但这里改变了调用该函数的对象的值,最好不要这样做。const A ^operator +(const A &j){A t; t.b=b+j.b; return t;}//定义一个临时对象t以防止修改调用该函数的两个操作数的值。friend const A ^operator +(const A &j, const int i); };//+操作符函数不应该改变调用该函数的两个操作数的值,所以这里将参数声 明为constconst A ^operator +(const A &j, const int i){A t;t.b=j.b+i; return t;}//定义友元函数以实现〃对象+类置类型〃这种类型的运算const A ^operator +(const int i, const A &j){A t; t.b=j.b+i;return t;} //因为类A没有私有成员,该函数也不访问类中的私有成员所以可以不用定义为类的友元,这个函数将实现,〃内置类型+对象〃的这种运算//对于这种操作符重载函数不能定义为类的成员函数,因为类的成员函数的第一个参数是指向类对象的一个this指针,在这里第一个参数是 内置类型不是类的对象,所以必须把这种操作符函数定义为非成员的形式。 int main(){ A m(l),n⑵,k;k=m+3;//调用operator +(const A &j,const int i)这个类的友元操作符函数,以实现对象和内置类型相加,该语句和operator +(m, 3)一样。cout<<m.b<<k.b; //输出14,这里没有改变对象m的值,这是应该的。k=operator +(m, 3); cout«m.b«k.b;//输出14。和上面m+3的语句一样,这是显示调用操作符函数的形式,记住操作符重载函数是一个函 数,只是他的名字比较特别,名字为operator加上后面的操作符而已k=m+n; //调用类成员操作符函数operator + (const A &j),实现两个对象相加,这条语句会自动转换为m.operator +(n)的形式调用操作符 函数。记住最左边的对象是调用类成员操作符函数的对象。cout<<m.b<<n.b<<k.b; //输出123,这里没有改变对象m和n的值,实现了对象m 和n相加k=n.operator +(m); cout<<m.b<<n.b<<k.b; //显示调用成员操作符函数operator +(const A &j)的形式,这种形式和语句n+m等价。k=4+m;//调用操作符函数operator +(const int i,const A &j)以实现内置类型和一个对象相加,语句和operator +(3,m)等价。cout<<m.b<<k.b; //输出 15。k=operator +(4,m); cout〈<m.b<〈k.b; } //输出 15,语句和+m 相同。

三:重载賦值运算符=

1.               注意重载赋值运算符和[],(),->运算符必须定义为类的成员函数。

2.               注意:如果程序不提供显示的赋值运算符则系统会提供一个默认的赋值运算符。

3.               什么时候重载赋值运算符:当类中含有指针成员时,一般都要重定义类的赋值运算符。

4.                 重载赋值运算符时应有处理语句n=m的情况。其中m是某一个类的对象。如果不处理这样的语句有时会出现问题具体什么问题有待调查。可以用this指针来做处理,比如有语句constA&operator(A &j)则可以用if(this==&j) return

这样的语句来处理,即比较当前调用赋值运算符函数的对象的地址和被赋值的对象的地址,如果地直相等说明是同一个对象。

5.                 重载赋值运算符时应返回一个对象。因为赋值运算符的左边是一个对象,所以重载赋值运算符应返回一个类的对象,为了避免不必要的开销,最好是返回一个类的对象的引用。

class A {public: int b; A(){b=l;} A(int i){ b=i;}const A & operator =(const A & j) //重载赋值运算符必须是类的成员,重载赋值运算符应返回一个类的对象的引用。{if ( this==&j) return *this;//用this和j的地址来检查是否是对自身的赋值的情况,如果调用赋值运算符函数的地址和被赋值的对象的地址相等,则说明是同一个对象,就返回当前对象。b=j.b; return *this;} };//const A & operator =(const A & j) {}//错误,重载赋值运算符时必须是类的成员函数。int main(){A m⑵;An; n=m;cout<<n.b;n=n; //对象对自已赋值的情况。 cout<<n.b; }
+和 -运算符的重载:

class Point  {  private:  int x; public:  Point(int x1){  x=x1;}  Point(Point& p)   {  x=p.x;}const Point operator+(const Point& p);//使用成员函数重载加号运算符friend const Point operator-(const Point& p1,const Point& p2);//使用友元函数重载减号运算符};  const Point Point::operator+(const Point& p){return Point(x+p.x);}Point const operator-(const Point& p1,const Point& p2){return Point(p1.x-p2.x);}
调用:
Point a(1);  Point b(2);a+b;  //正确,调用成员函数a-b;  //正确,调用友元函数a+1;  //正确,先调用类型转换函数,把1变成对象,之后调用成员函数a-1;  //正确,先调用类型转换函数,把1变成对象,之后调用友元函数1+a;  //错误,调用成员函数时,第一个操作数必须是对象,因为第一个操作数还有调用成员函数的功能1-a;  //正确,先类型转换 后调用友元函数

++和--运算符的重载:
class Point  {  private:  int x; public:  Point(int x1){  x=x1;}  Point operator++();//成员函数定义自增const Point operator++(int x); //后缀可以返回一个const类型的值friend Point operator--(Point& p);//友元函数定义--friend const Point operator--(Point& p,int x);//后缀可以返回一个const类型的值};  Point Point::operator++()//++obj{x++;return *this;}const Point Point::operator++(int x)//obj++{Point temp = *this;this->x++;return temp;}Point operator--(Point& p)//--obj{p.x--;return p;         //前缀形式(--obj)重载的时候没有虚参,通过引用返回*this 或 自身引用,也就是返回变化之后的数值}const Point operator--(Point& p,int x)//obj--{Point temp = p;p.x--;return temp;         // 后缀形式obj--重载的时候有一个int类型的虚参, 返回原状态的拷贝}
函数调用:
<PRE class=cpp name="code">Point a(1);Point b(2);a++;//隐式调用成员函数operator++(0),后缀表达式++a;//隐式调用成员函数operator++(),前缀表达式b--;//隐式调用友元函数operator--(0),后缀表达式--b;//隐式调用友元函数operator--(),前缀表达式cout<<a.operator ++(2);//显式调用成员函数operator ++(2),后缀表达式cout<<a.operator ++();//显式调用成员函数operator ++(),前缀表达式cout<<operator --(b,2);//显式调用友元函数operator --(2),后缀表达式cout<<operator --(b);//显式调用友元函数operator --(),前缀表达式 </PRE>

重载下标运算符[ ]
class Point  {  private:  int x[5]; public:  Point(){for (int i=0;i<5;i++){x[i]=i;}} int& operator[](int y);};  int& Point::operator[](int y){static int t=0;if (y<5){return x[y];}else{cout<<"下标出界";return t;}}
调用:
Point a;for (int i=0;i<10;i++){         cout<<a[i]<<endl;//无论i下标是否越界,每当使用a[i]时,都会调用[]的重载}a[0]=10;

重载运算符( )

class Point  {  private:  int x; public:  Point(int x1){  x=x1;}  const int operator()(const Point& p);};  const int Point::operator()(const Point& p){return (x+p.x);}

调用:Point a(1);Point b(2);cout<<a(b);
重载输入输出操作符<< >>

class Point  {  private:  int x; public:  Point(int x1){  x=x1;} friend ostream& operator<<(ostream& cout,const Point& p);//使用友元函数重载<<输出运算符friend istream& operator>>(istream& cin,Point& p);//使用友元函数重载>>输出运算符};  ostream& operator<<(ostream& cout,const Point& p){cout<<p.x<<endl;return cout;}istream& operator>>(istream& cin,Point& p){cin>>p.x;return cin;}

调用:Point a(1);Point b(2);cin>>a>>b;cout<<a<<b<<endl;



原创粉丝点击