2013.7.18 运算符的加载
所谓函数的重载是值完成不同功能的函数具有相同的函数名。
C++编译器根据函数的实参来确定应该调用哪一个函数的。
注意:
- 定义的重载函数必须具有不同的参数个数,或者不同的参数类型。只有这样编译系统才有可能根据不同的参数去调用不同的重载函数。
- 仅返回值不同时,不能定义为重载函数。
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;
}
若不定义字符串的析构函数,则可以不定义它的拷贝的构造及赋值函数,若定义了析构函数,必须重新定义这两个成员函数。
原则:每个对象都有自己的独立空间。

浙公网安备 33010602011771号