现在的位置: 首页 > 综合 > 正文

(十四)运算符的重载

2013年10月08日 ⁄ 综合 ⁄ 共 5231字 ⁄ 字号 评论关闭

1、重载与重写的区别:

     重写:函数名和参数的类型及个数完全一样的几个函数,当调用时会发生“就近”(当前作用域)覆盖掉远的函数的情况,就是重写,重点”覆盖“!!!

     重载:函数名一样,但参数个数或类型不一样时,调用时就匹配其参数个数或类型等”选择“一个最适合的,就是重载。重点”选择“!!!

2、运算符重载:一般对于类类型的对象定义运算,这种叫运算符重载。

       去处符重载的定义:   返回类型   operator运算符(参数列表)

       一、只能是对已有的运算符进行重载,不能发明新的运算符。

       二、不能修改原运算符的优先级和操作数的个数。比如X比+要优先,我们不能更改,X是二元操作,不能改成三元操作。

       三、定义中对于字母字符的去处符如new 、delete要与operator至少保持一个空格,对于其它,空格是可选的。

       四、不能参与重载的运算符有: ::    ?:    .       解析*       sizeof

       五、重载的函数不一定是类的成员函数。

       例: class A{ public:  .....

                                            bool operator<(const A& another){..................}

                                           .....................

                              };

          A  a(3),b(4);

          if(a<b)....;//相当于   a.operator<(a)

3、全局运算符函数:除类成员函数可重载外,一般函数也可以重载。

      这时,如果运算符重载函数要访问类的私有成员时,可以声明此函数为友元,即可访问私有。但一般这种情况都声明为类的成员重载运算符更为恰当!!!

      注意:如果运算符重载函数的第一个操作数是基本类型,则必须声明为“全局运算符函数”!!

                 另外强调的是,内联函数必须和它的声明保持在一个文件中,如果声明在头文件中,则其它定义也必须在头文件中,不准在源代码cpp中。

4、重载的几种情况:

     二元:  类成员:      返回值  operator运算符(右操作数)

                一般函数:     返回值  operator运算符(左操作数,右操作数)//左操作数为基本类型时,必须用一般函数

    一元:  类成员:      返回值  operator运算符( )

                一般函数:     返回值  operator运算符( 操作数 )

    可以看出一般函数比类成员要少一个参数。

   特殊:前置与后置++ --

    前置和一元的情况的一样。

     后置为了区分前置,多加了一个参数int

     后置:  类成员:      返回值  operator运算符(int)

                一般函数:     返回值  operator运算符(左操作数,int)

5、赋值运算符重载。

     如果没有提供重载的赋值运算符函数来复制类的对象,编译器就会提供默认版本的operator()。

class A{
	public:
	int a;
	A(const A& another)           {a=another.a;  cout<<"copy"<<endl;}
	A(int num)                    {a=num;        cout<<"number"<<endl;}
	A& operator=(const A& another){a=another.a;	 cout<<"="<<endl;	}
};
int main(int argc, char *argv[]){
	A b(3),c=b;//输出number,,然后再copy,因为这里是"创建“对象并”初始化“对象 
	c=b;//因为对象C已经存在,只是使用了赋值。 
	return 0;
}

        一、上面返回值用了引用,更加高效。

        二、第一个c=b用的copy,第二个用的是=。

        注意:拷贝构造函数:是用一个已知对象去“创建”和“初始化”另一个未建立的对象。

                   赋值运算符重载函数:用一个已知的对象去改写另一个已知的对象。

                  两者,编译器都会提供一个默认版本。 但两者的适用范围和适用对象都是不一样的,因此上面的b=c分别调用两个不同的函数。

       同时上面程序有一个bug,就是没有返回值,因为赋值运算有a=b=C的情况,因此返回值是必须的。

6、赋值运算符应注意:

      它总是应该去检查两个对象是否相等。一、如果右值对象为空,左值将为空;二、如果左值与右值已经相等,不再赋值;三、总应该有拷贝构造函数。

      因为如果它的成员是指针,如果只是指针复制,将指向同一目标,左值与右值将相互依赖存在而产生 BUG。

      因此:如果类的函数在自由存储区中动态分配内存,就总是应该实现副本构造函数、赋值运算符和析构函数。

7、exit(1)是什么意思?

     Exit用于在程序运行的过程中随时结束程序,函数原型void exit(int status),exit的参数是返回给操作系统的。
           这个参数识了应用程序的一些运行信息。
           和操作系统有关一般是0为正常退出,非0为非正常退出。
           非0的数字可以是自己拟定的可以是任意一个整型数字。   退出进程,会根据其返回的int值做善后处理 。

8、经济化运算符的重载。

有时我们可以根据前面的重载,在另一个重载中调用前面的,达到经济节约代码的效果:

class A{
	int a;
	public:
	A(int m=1){a=m;}
	void show(){cout<<a<<endl;}
	int getvalue()const{return a;}
	A& operator +=(const A& another){
		a+=another.getvalue();
		return *this;
	}
	A& operator +(const A& another)const {
		return A(*this)+=another;//返回的是临时值,因为本身就是中间值 
	}
	A& operator =(const A& another){
		a=another.getvalue();
		return *this;
	}
};
int main(int argc, char *argv[]){
	A a(3),b(4),c;
	c=a+b;
	a.show();// 查看是否修改了a,结果:没有修改。 
	return 0;
}

可以看到+中调用了+=,节约了代码。同样>和<、*和*=,/和/=、-和-=等等,可以相互利用一下。

9、operator [](int value) 下标的重载。数组的重载,因为有些是链表,有时有必要进行“元素化”成数组。
注意两点:
   一、lvalue(左值)与重载下标的混合:有时有load[0]=load[2];
       如果[]重载的返回值是临时对象的话,作为左值就会出问题。这里返回值用引用(不能是临时变量的引用)可作为左值。
   二、const的重载:
       有时,对象可作左值,有时又不能作左值,如何满足这两种 情况。
       用返回参数的性质来改变,一个用const参数,一个用非const参数,这样就可以重载了。
       但这两个在一个类中是没法共存的。。。。

10、类型转换运算符重载

        一个类转换为另一个类型,针对类类型转为其它类型:  operator  Type();

                   注意:1、源对象是本类类型;2、目标(返回值类型)不用写,始终是type;3、有返回值。

         当Box类对象theBox有下面时: operator double() const;//定义转换类型重载,返回double类型

                                      double   a=theBox;//theBox对象会隐式地转换成double型,然后给a赋值。

           上面相当于   double a=static_cast<double>(theBox);

                          或: double(theBox);

            构造函数成有转换功能:   A(const     B&   another);//把another转换到A类型

                                                           operator   B();  //把A类型转换到B类型

           有时,转换类型时编译器不知道选择哪个函数,为了去除转换运算符和构造函数之间的这种模糊性,可以把构造函数声明为explicit(显式)。

11、递增和递减重载。

        对于前置就是前面用的默认即可,不过这个一般返回的是引用。

        对于后置,须构造一个对象副本保存原对象,然后原对象递增或递减,再返回那个保存原值的副本即可。因此,它返回的是对象。

12、智能指针,又称迭代器。

        它实质上是一个类构造的一个对象,只不过个这对象有点特殊,它重载了指针的一些功能,形式上象指针一样使用,事实上象指针一样地操作。

        就象一个类重载了+,但实际上内部的代码用的是减法的代码,因此,它完成的是减法的功能,智能指针一样,实质是对象,但重载中完成了指针的功能。

       因此,它一会是对象在用,一会又象是作指针在用,感觉很智能。

      看一个例子:TruckLoad是一个链表类,这个链表有节点,节点类是Package,每个节点有数据Box类,和下一个节点的指向即Package*.

                             为此,我们定义一个Box智能指针类,它可作Box对象,也可作为Box指针来使用

class BoxPtr{
	public:
	BoxPtr(TruckLoad& load){pBox = rLoad.getFirstBox();}//构造只能由TruckLoad来产生 
	Box& operator*()const{   //重载解除指针,返回应该是对象,为了高效返回引用 
		if(pBox)
			return *pBox;
		else{
			cout << endl << "Dereferencing null BoxPtr";
			exit(1);
		}
	}
	Box* operator->()const{	return pBox;}//重载->,返回一个指针 
	Box* operator++(){ return pBox = rLoad.getNextBox();}//重载++,返回下一个Box的指针 
	const Box* operator++(int){  //重载后置++,先下移,返回原指针副本 
		Box* pTemp = pBox;
		pBox = rLoad.getNextBox();
		return pTemp;
	}
	operator bool(){ return pBox != 0;}//重载bool,以便if中判断 
	
	private:
	Box* pBox;
	TruckLoad& rLoad;
	//防止其它来构造,放入私有表示禁止
	BoxPtr();
	BoxPtr(BoxPtr&);
	BoxPtr operator=(const BoxPtr&);	
};

      可以看到,这个类可以作为Box类来使用,也可当Box的对象的指针来使用。它们的重载都是返回的指针。把构造函数放进私有可以防止其它调用来构造。

      它只能由TruckLoad类来构造。

      operator->()的重载:BoxPtr  pLoadBox(aTruckLoad);

                                           double  boxVol=pLoadBox->volume();//形式上象是使用了指针,这里重载了->,等价于:boxVol=(pLoadBox.operator->())->volume();

      返回值是const防止重复重载如pLoadBox++++,另外,bool重载是方便使用 if(pLoadBox){....}判断的情形。

13、 引用临时对象是个错误,特别是通过地址引用临时对象的错误,是因为临时对象的生命结束在引用临时对象前

          那时访问的地址是一个无效的地址。
14、重载运算符New和Delete
        一、重载new,就必须重载delete;
        二、重载的原因:使某类对象的内存分配和释放更快、更经济,当需要为巨量对象分配内存空间且每个对象都需要少量的内存时,就应重载new delete
        例:class   A { public:         void*   operator   new(size_t  Size);
                                                       void    operator   delete(void*   Object,    size_t   Size);  }
         要在针对类的运算符函数中调用全局运算符,可以使用作用域解析运算符::。  比如类中重载new要使用全局的new
                      void*    operator  new(size_t   size){   .......;
                                                                                       pSpace= ::new  char(size);.................}
         

抱歉!评论已关闭.