运算符重载

来源:互联网 发布:长春java 编辑:程序博客网 时间:2024/05/18 03:24


接触C++只有3个月,只是写给自己看的啦 0 0.


OOP 第二次读书报告


1)你可以重载运算符为成员函数和非成员函数,应该选择哪个?

2)重载运算符为成员函数和非成员函数的特点?

3)如何理解“逐个成员赋值memberwise assignment”(与“逐个字符赋值bitwise assignment”相对应)?


1

      ·单目运算符最好重载为类的成员函数,双目运算符最好重载为类的非成员函数:

 

const complex operator +(const complex &r,const complex &s);//双目运算符重载为非成员函数(全局函数)friend const complex operator +(const complx &r,const complex &s);//双目运算符重载为非成员函数(友元函数) const complex operator -()const;//单目运算符重载为成员函数


  例外:

  不要重载为非成员函数

 

  当然,单目运算符也可以重载为类的非成员函数,双目运算符也可以重载为类的成员函数:

 

const complex operator +(const complex &x)const;//双目运算符重载为成员函数const complex operator -(const complex &x);//单目运算符重载为非成员函数(全局函数)friend const complex operator -(const complex &x); //单目运算符重载为成员函数(友元函数)

 

     ·如果第一个运算符不是该类的一个对象,而是其它类的对象或者普通变量等,那么不能使用重载为成员函数,因为重载为成员函数有一个隐藏的this指针,第一个参数默认为该类的对象:

 

const complex operator +(int r,const complex &s);//第一个参数不是此类的对象,重载为非成员函数(全局函数)friend const complex operator -(int r,const complex &s);//第一个参数不是此类的对象,重载为非成员函数(友元函数)


    ·考虑不同参数顺序,使用友元非成员函数:

 

friend const complex operator -(const complex &r,int s);friend const complex operator -(int r,const complex &s);
  

    ·如果运算操作需要修改运算数自身,那么最好使用成员函数:

 

const Integer& operator++();//前缀运算符 

    ·操作数希望存在隐式类型转化,使用非成员函数:

 

const Integer operator +(const Integer &r,const Integer &s);Integer x(3),y(7),z;z = x + y;//okz = x +3;//okz = 3 + y;//okz = 3 + 7;//ok

     如果使用成员函数:

 

const Integer operator +(const Integer &x)const;Integer x(3),y(7),z;z = x + y; //okz = x + 3; //okz = 3 + y; //error


  对于z = x + 3,编译器用构造函数把3构造成int对象。

  对于z = 3 + y, 编译器从左到右读取,看到3的时候就确定用整数的加减,再发现右边的不是整数,所以需要把对象变成整数的手段,但是对象里没有这个手段。

 

     · 类型转换函数定义为成员函数:

 

operator int();

    

 

2

  成员函数:

 

  有一个隐藏的参数,this指针。对于一元运算符,不需要参数;对于二元运算符,只需要that参数,以此类推。

这样也就限制了第一个参数必须为类的对象,对于整数类,z = 3 + y 就是错误的。

 

complex x(1,2), y(3,5), z;z = x + y;//等价于x.operator+(y) 

   其中左边的算子决定使用哪个重载的+

 

  非成员函数:

 

  不存在隐藏的this指针,需要把所有运算符按照顺序传入。

  在开发类时使用非成员函数进行运算符重载,一般将其设置为友元函数。

  在已有类上,如果有办法接触到其成员,也就可以在不修改的前提下利用非成员函数添加运算符重载,对第一个参数的限制也就降低。但是在一定程度上也破坏了C++的封装性。

 

complex x(1,2), y(3,5), z;z = x + y;//等价于operator+(x,y)

3

 

  当你去调一个参数是对象本身的函数时,在这个时候,就会发生拷贝构造。

  但是在写这种拷贝构造函数的时候,传参数的时候却应该传入对象的引用,如果这里传的不是引用而是对象本身,那么在函数接收参数的时候(入栈过程),会有产生一个临时对象来接收这个参数,这个过程又是一次拷贝构造,因此函数反复调用自己,形成死循环。所以必须传入引用。

 

  如果定义了自己拷贝构造函数:

<span style="font-family:宋体;font-size: 10.5pt;">   complex::complex(const complex &x); </span>


  那么在执行下述代码时:

complex b(1,2);complex a = b;//等价于complex a(b); 

  就会调用上面自己的拷贝构造函数。

  这就叫做memberwise copy.

 

  如果你没有写那个自定义的拷贝构造函数,系统会为你提供一个拷贝构造函数。发生的过程是先用b的构造函数去构造一个b对象,然后再按字节复制给a对象。(直接将一段内存上的数据拷贝到另一段内存上,类似于memcpy

  这就叫做bitwise copy.

 

  按字节拷贝存在这些问题:

  1.对于指针直接拷贝,导致两个指针指向同一块内存,这样如果我们同时调用它们,就会让同一块内存析构两次。我们所希望的是拷贝后的指针指向新的那块内存。

  2.如果拷贝构造函数内部还存在其它对象,那么就会继续调用拷贝构造函数,不断递归,但是对于按字节拷贝来说,就没有这种操作方式。

 

  如果你没有将其写成初始化形式,而是写成了赋值形式:

 

complex b(1,2);complex a;a = b;


  在读到complex a这一行时,没有接收到任何参数,调用的是a的缺省构造函数,下一行a = b调用的将是自己的赋值重载函数。

如果没有自己的赋值重载函数,将发生bitwise copy.

 

以下是一个测试代码:

 

#include<iostream>using namespace std;class complex{int r;int i;public:complex& operator =(const complex &x){cout<<"complex& operator ="<<endl;if(this!=&x){this->r = x.r;    this->i = x.i;}return *this;}friend ostream& operator <<(ostream& out, const complex &x){out<<x.r<<" + "<<x.i<<"i"<<endl;}complex(int x,int y):r(x),i(y) { cout<<"common constructor"<<endl; };complex(){ cout<<"default constructor"<<endl; };complex(const complex &x){cout<<"copy constructor"<<endl;if(this!=&x){this->r = x.r;this->i = x.i;}}};int main(){complex b(1,2);cout<<"b = "<<b;complex a = b;cout<<"a = "<<a;complex c;c = b;cout<<"c = "<<c;}

运行结果:


   

    

先发生b的普通构造,再是a的拷贝构造,最后是c的缺省构造与赋值。


注释掉拷贝构造函数:

   

   

   不发生拷贝构造,但a依旧得到了值。

 

  注释掉赋值的重载:

   

   

    不发生重载,但c依旧得到了值。



0 0
原创粉丝点击