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<<'\t'<<"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()<<'\t';
	cout <<"s2= "<< s2.GetS()<<'\n';

  


 

一个字符串类

在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;
}

  

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

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

  

 

 

 

posted @ 2013-07-18 22:00  ymonke  阅读(302)  评论(0编辑  收藏  举报