zoukankan      html  css  js  c++  java
  • 2013.7.18 运算符的加载

    所谓函数的重载是值完成不同功能的函数具有相同的函数名。

    C++编译器根据函数的实参来确定应该调用哪一个函数的。

    注意:

    1. 定义的重载函数必须具有不同的参数个数,或者不同的参数类型。只有这样编译系统才有可能根据不同的参数去调用不同的重载函数。
    2. 仅返回值不同时,不能定义为重载函数。
    int sum,a=3,b=2;
    sum=a+b; 
    (int)=(int)+(int)
    //可以
    char str[4],c1[2]="a",c2[2]="b";
    str=c1+c2;
    (char*)=(char*)+(char*)
    //编译系统中的运算符“+”本身不具有这种运算。若使上式可以运算,必须重新定义“+”运算符,这种重新加载的过程成为运算符的重载。
    class A
    {
    float x,y;
    public:
    A(float a=0,float b=0){x=a;y=b}
    }
    void main(void)
    {
    A a(2,3),b(3,4),c;
    c=a+b;
    }
    //两对象不能使用+,必须重新定义。  

    运算符的重载从另外一个方面体现了OOP技术的多态性,且同一运算根据不同的运算对象可以完成不同的操作。

    为了重载运算符必须重新定义一个函数,由这个函数完成这个重载运算符应该完成的操作。它通常是类的成员函数或者是友元函数。运算符的操作数通常也是该类的对象。


    一、重载为类的成员函数

    格式如下:

    <类名>operator<运算符>(<参数表>)

    {

    函数体

    }

    A operator +(A &);//重载了类的‘+’运算符,其中operator和其后的运算符一起构成函数名。

    class A
    {	int i;
    public:A(int a=0)	{ i=a;	}
    	void Show(void){	cout<<"i="<<i<<endl;	}
    	void AddA(A &a,	A &b)	//利用函数进行类之间的运算
    	{	i=a.i+b.i;	}
    };
    void main(void)
    {	A a1(10),a2(20),a3;
    	a1.Show ();
    	a2.Show ();
    //	a3=a1+a2;			//不可直接运算
    	a3.AddA(a1,a2);		       //调用专门的功能函数
    	a3.Show ();
    
    class A
    {	int i;
    public:A(int a=0){ i=a;	}
    	void Show(void){	cout<<"i="<<i<<endl;	}
    	void AddA(A &a,   A &b)	//利用函数进行类之间的运算
    	{	i=a.i+b.i;	}
    	A operator +(A &a)	//重载运算符+
    	{	A   t;	t.i=i+a.i;	return t;	}
    };
    void main(void)
    {	A a1(10),a2(20),a3;
    	a1.Show ();
    	a2.Show ();
    	a3=a1+a2;	//重新解释了加法,可以直接进行类的运算 相当于a3=a1.operator+(a2)
    	a3.AddA(a1,a2);		//调用专门的功能函数
    	a3.Show ();
    

      重载运算符和一般函数的比较

    void  AddA(A &a,   A &b)      
    {	i=a.i+b.i;	}
    
    函数调用:
    a3.AddA(a1,a2);
    
    
    A operator +(A &a)	
    {   A   t;	
         t.i=i+a.i;	
         return t;
    }
    函数调用
    a3=a1+a2;
    a3=a1.operator+(a2)
    

    重新定义运算符,由左操作符调用右操作符。最后将函数返回值赋给运算结果的对象。

    class A
    {	int i;
    public:A(int a=0){ i=a;	}
    	void Show(void){	cout<<"i="<<i<<endl;	}
    	void AddA(A &a,   A &b)	//利用函数进行类之间的运算
    	{	i=a.i+b.i;	}
    	A operator +(A &a)	//重载运算符+
    	{	A   t;	t.i=i+a.i;	return t;	}
    };
    void main(void)
    {	A a1(10),a2(20),a3;
    	a1.Show ();
    	a2.Show ();
    	a3=a1+a2;	//重新解释了加法,可以直接进行类的运算
    	a3.AddA(a1,a2);		//调用专门的功能函数
    	a3.Show ();
    }
    

    当用成员函数实现运算符的重载时,运算符重载函数的参数只能用两种情况:没有参数或带有一个参数。对于一个操作数的运算符(如++),在重载这种运算符时,通常不能有参数;而对于有二个操作数的运算符,只能带有一个参数。着参数可以是对象,对象的引用,或其他类型的参数。在C++中不允许重载有三个操作数的运算符。

    运算符的优先级和结合律是不能改变的。


    单目运算符的重载

    只具有一个操作数的运算符为单目运算符,最常用的为++及——。

    A    a;
    ++a;
    a++;
    
    
    A    a, b;
    b=++a;
    b=a++;
    
    可以看出,虽然运算后对象a的值一致,但先自加或后自加的重载运算符函数的返回值不一致,必须在重载时予以区分。
    

    ++为前置运算时,它的运算符重载函数的一般格式为:

    <type> operator ++( )

    {    ......;}

    ++为后置运算时,它的运算符重载函数的一般格式为:

    <type>  operator ++(int) 

    {    ......;}

    A    a, b;
    b=++a;  A  operator ++(   ){  ....  }
    b=a++; A  operator ++(int){  ....  }
    
    class A
    { float x, y;
    public:
       A(float a=0, float b=0){  x=a;  y=b;  }
       A operator ++( ){A  t;  t.x=++ x; t.y=++y;  return t;//return *this}
      A operator ++(int) {  A  t;  t.x=x++;  t.y=y++;  return t;}
    };
    void main(void)
    {  A   a(2,3), b;
        b=++a;
        b=a++;
    }
    

      用成员函数实现运算符的重载时,运算符的左操作数为当前对象,并且要用到隐含的this指针。运算符重载函数不能定义为静态的成员函数,因为静态的成员函数中没有this指针

      


    二、运算符重载为友元函数

    函数重载为友元函数时,是由一个操作数调用另外一个操作数。即函数的实参只有一个或者没有。 

    c=a+b;实际上是c=a.operator+(b);

    c=++a;实际上是c=a.operator++(  );

    友元函数就是在类外的普通函数,与一般函数的区别是可以调用类中的私有或者保护数据。

    将运算符的重载函数定义为友元函数,参与运算的对象全部成为函数参数。 

    c=a+b;实际上是 c=operator+(a, b);

    c=++a;实际上是 c=operator++(a);

    对双目运算符,友元函数有2个参数,对单目运算符,友元函数有一个参数。有些运算符不能重载为友元函数,它们是:=,(),[ ],->等

    格式为:

    friend <类型说明> operator<运算符>(<参数表>)

    {......}

    c=a+b;   //  c=operator+( a, b)

    friend   A   operator + (A &a, A &b)

    {.....}

    ++为前置运算时,它的运算符重载函数的一般格式为:

    A operator ++(A &a)

    {    ......;}

    ++为后置运算时,它的运算符重载函数的一般格式为:

    A operator ++(A &a, int) 

    {    ......;}

    class A
    {	int i;
    public:public:
    	A(int a=0)	{ i=a;	}
    	void Show(void)	{	cout<<"i="<<i<<endl;	}
    	friend A operator++(A &a){ a.i++;  retrurn a;}
    	friend A operator++(A &a, int n)
    	{  A  t;		t.i=a.i;   a.i++;         return t;	}                
    };
    void main(void)
    {	A a1(10),a2,a3;
    	a2=++a1;                 //相当于a2=operator++(a1)
    	a3=a1++;                 //相当于a3=operator++(a1,int)
    	a2.Show();	a3.Show ();
    

      

     对双目运算符,重载为成员函数时,仅一个参数,另一个被隐含;重载为友元函数时,有两个参数,没有隐含参数。

             一般来说,单目运算符最好被重载为成员函数;对双目运算符最好被重载友元函数。


    三、转换函数

    转换函数就是在类中定义一个成员函数,其作用是将类转换为某种数据类型。

    1. 转换函数必须是类的成员函数。

    2. 转换函数的调用是隐含的,没有参数。

    A :: operator float ( )

    {  return  x+y;  }

    class Complex{
    	float Real,Image;
    public:
         Complex(float real=0,float image=0)
    	{	Real=real;	Image=image;	}
          void Show(void)	
           {cout<<"Real="<<Real<<'	'<<"Image="<<Image<<endl;	}
     operator float();	//成员函数,定义类转换 Complex->float
    };
    Complex::operator float ()
    {	return Real*Real+Image*Image;}
    void main(void)
    {	Complex c(10,20);
    	c.Show ();
    	cout<<c<<endl;//可以直接输出c,因为已经进行类型转换	
    }

    注意,转换函数只能是成员函数,不能是友元函数。转换函数的操作数是对象。转换函数可以被派生类继承,也可以被说明为虚函数。


    赋值运算符和运算符的重载

    同类型的对象间可以相互赋值,等同于对象的各个成员的一一赋值。

    A   a(2,3), b;

    b=a;

    但当对象的成员中使用了动态的数据类型时(用new开辟空间),就不能直接相互赋值,否则在程序的执行期间会出现运行错误。

    这时,利用编译系统的默认赋值无法正确运行程序,必须重载赋值运算符“=”,即重新定义“=”。

    格式为:

    <函数类型>   <ClassName>::operator=(<参数表>)

    赋值运算符必须重载为成员函数。

    class  A{
    	char *ps;
    public:	A( ){ ps=0;}
    A(char *s ){	ps =new char [strlen(s)+1];  strcpy(ps,s);}
    ~A( ){ if (ps) delete ps;}
    void Show(void) {  cout<<ps<<endl;}
    A&  operator=(A  &b);
    };
    void  main(void )
    { A s1("China!"),s2("Computer!");
      s1.Show();  s2.Show();	
      s2=s1;				
      s1.Show();  s2.Show();	
    }
    A &A::operator  = (  A  &b)//重载赋值运算符
    {    if  ( ps )  delete  [ ] ps;
        if  ( b.ps) 
        {	ps = new  char [ strlen(b.ps)+1];
    	strcpy( ps, b.ps);
         }
         else 	 ps =0;
         return *this;
    }
    
    class  A{
    	char *ps;
    public:	A( ){ ps=0;}
    	A(char *s ){	ps =new char [strlen(s)+1];	strcpy(ps,s);	}
    	~A( ){ if (ps) delete ps;}
    	char *GetS( ) {return ps;}
    	A &   operator  = (  A  &b);//重载赋值运算符
    };
    A &A::operator  = (  A  &b)//重载赋值运算符
    {    if  ( ps )  delete  [ ] ps;
          if  ( b.ps) {	ps = new  char [ strlen(b.ps)+1];	strcpy( ps, b.ps);	}
          else 		 ps =0;
         return *this;
    }
    void  main(void )
    {	A s1("China!"),s2("Computer!");
    	s2=s1;					
    	cout <<"s1= "<< s1.GetS()<<'	';
    	cout <<"s2= "<< s2.GetS()<<'
    ';
    

      


    一个字符串类

    在C++中,系统提供的字符串处理能力比较弱,都是通过字符处理函数来实现的,并且不能直接对字符串进行加法、减法,字符串的拼接,字符串之间的相互赋值等操作。可以通过应用C++提供的运算符重载机制,可以提供字符串的直接操作能力,使得字符串的操作与一般的数据一样方便。

    class String

    {     int Length;//字符串长度

          char *Sp;  //字符串在内存中的首地址

     public:

           .....

    }

    可见,字符串类只定义了指针,并没有开辟具体的空间以存放字符串的内容,所以,无论是构造、析构还是加减等,均需要考虑动态开辟空间的问题,这也是字符串类的难点。

    class  String{
    	int Length;			//字符串的长度
    	 char  *Sp;			//指向字符串的指针
    public:	String(){Sp=0;Length=0;}	//缺省的构造函数
    	String( char *s)		//以一个字符串常量作为参数
    	{	Length = strlen(s);	
    		Sp=new char[Length+1];	
    		strcpy(Sp,s);	}
    	~String(){  if(Sp) delete [  ] Sp;  }
    	friend String operator +(String &,String &);//友元函数重载+
    	String & operator =(String &);//成员函数重载赋值=
                  String  (String &s);  //拷贝的构造函数(必须有)
    };
    void main(void)
    {	String str1("China");
    	String str2("CCTV");
    	String str3;
    	str3=str1+str2;		str2=str1;
    	cout<<str3<<endl;
    }
    String & String:: operator =(String &str)
    {	if (Sp)
    		delete []Sp;
    	Length=str.Length ;
    	Sp =new char[Length +1];
    	strcpy(Sp,str.Sp);
    	return *this;
    }
    String  operator +(String &str1,String &str2)
    {
    	String str;
    	str.Length=str1.Length+str2.Length;
    	str.Sp=new char[str.Length +1];
    	strcpy(str.Sp,str1.Sp);
    	strcat(str.Sp,str2.Sp);
    	return str;
    }
    

      

    若不定义字符串的析构函数,则可以不定义它的拷贝的构造及赋值函数,若定义了析构函数,必须重新定义这两个成员函数。

    原则:每个对象都有自己的独立空间。

      

  • 相关阅读:
    并查集图冲突hdu1272
    CentOS 7通过yum安装fcitx五笔输入法
    近期的技术问题让云供应商进行预设加密
    POJ 1166 The Clocks (暴搜)
    windows中的mysql修改管理员密码
    Visio画UML类图、序列图 for Java
    js中的时间与毫秒数互相转换
    java.lang.OutOfMemoryError: unable to create new native thread 居然是MQ问题
    WEB移动应用框架构想(转载)
    Android SDK安装教程
  • 原文地址:https://www.cnblogs.com/ymonke/p/3199458.html
Copyright © 2011-2022 走看看