运算符重载
函数的重载
所谓函数的重载是指完成不同功能的函数可以具有相同的函数名。
C++的编译器是根据函数的实参来确定应该调用哪一个函数的。
int fun(int a, int b)
{ return a+b; }
int fun (int a)
{ return a*a; }
void main(void)
{ cout<<fun(3,5)<<endl;
cout<<fun(5)<<endl;
}
1、定义的重载函数必须具有不同的参数个数,或不同的参数类型。只有这样编译系统才有可能根据不同的参数去调用不同的重载函数。
2、仅返回值不同时,不能定义为重载函数。
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; 两对象不能使用+,必须重新定义+
}
运算符重载就是赋予已有的运算符多重含义。C++通过重新定义运算符,使它能够用于特定类的对象执行特定的功能
运算符的重载从另一个方面体现了OOP技术的多态性,且同一运算符根据不同的运算对象可以完成不同的操作。
为了重载运算符,必须定义一个函数,并告诉编译器,遇到这个重载运算符就调用该函数,由这个函数来完成该运算符应该完成的操作。这种函数称为运算符重载函数,它通常是类的成员函数或者是友元函数。运算符的操作数通常也应该是类的对象。
重载为类的成员函数
格式如下:
<类名> operator<运算符>(<参数表>)
{函数体}
operator: 关键字
参数表: 运算的对象
其中: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 ();
}
重载运算符与一般函数的比较:
相同:1)均为类的成员函数;2)实现同一功能
总结:
重新定义运算符,由左操作符调用右操作符。最后将函数返回值赋给运算结果的对象。
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++中不允许重载有三个操作数的运算符
2、在C++中,允许重载的运算符列于表13.1中。
3、在C++中不允许重载的运算符列于表13.2。
4、只能对C++中已定义了的运算符进行重载,而且,当重载一个运算符时,该运算符的优先级和结合律是不能改变的。
单目运算符的重载
只具有一个操作数的运算符为单目运算符,最常用的为++及--。
A a;
++a;
a++;
A a, b;
b=++a;
b=a++;
可以看出,虽然运算后对象a的值一致,但先自加或后自加的重载运算符函数的返回值不一致,必须在重载时予以区分。
++为前置运算时,它的运算符重载函数的一般格式为:
<type> operator ++( )
{ ......;}
++为后置运算时,它的运算符重载函数的一般格式为:
<type> operator ++(int)
{ ......;}
用成员函数实现运算符的重载时,运算符的左操作数为当前对象,并且要用到隐含的this指针。运算符重载函数不能定义为静态的成员函数,因为静态的成员函数中没有this指针。
运算符重载为友元函数
运算符重载为成员函数时,是由一个操作数调用另一个操作数。
友元函数是在类外的普通函数,与一般函数的区别是可以调用类中的私有或保护数据。
将运算符的重载函数定义为友元函数,参与运算的对象全部成为函数参数。
对双目运算符,友元函数有2个参数,对单目运算符,友元函数有一个参数。有些运算符不能重载为友元函数,它们是:=,(),[ ],->等
格式为:
friend <类型说明> operator<运算符>(<参数表>)
{......}
c=a+b; // c=operator+( a, b)
friend A operator + (A &a, A &b)
{.....}
对双目运算符,重载为成员函数时,仅一个参数,另一个被隐含;重载为友元函数时,有两个参数,没有隐含参数。
一般来说,单目运算符最好被重载为成员函数;对双目运算符最好被重载友元函数。
转换函数
转换函数就是在类中定义一个成员函数,其作用是将类转换为某种数据类型。
class A
{ float x, y;
public:
A(float a, float b){ x=a; y=b; }
};
void main(void)
{ A a(2,3);
cout<<a<<endl;
}
注意,转换函数只能是成员函数,不能是友元函数。转换函数的操作数是对象。转换函数可以被派生类继承,也可以被说明为虚函数。
赋值运算符与赋值运算符重载 “=”
同类型的对象间可以相互赋值,等同于对象的各个成员的一一赋值。
但当对象的成员中使用了动态的数据类型时(用new开辟空间),就不能直接相互赋值,否则在程序的执行期间会出现运行错误。
这时,利用编译系统的默认赋值无法正确运行程序,必须重载赋值运算符“=”,即重新定义“=”。
格式为:
<函数类型> <ClassName>::operator=(<参数表>)
赋值运算符必须重载为成员函数。
一个字符串类
在C++中,系统提供的字符串处理能力比较弱,都是通过字符处理函数来实现的,并且不能直接对字符串进行加法、减法,字符串的拼接,字符串之间的相互赋值等操作。可以通过应用C++提供的运算符重载机制,可以提供字符串的直接操作能力,使得字符串的操作与一般的数据一样方便。
class String
{ int Length;//字符串长度
char *Sp; //字符串在内存中的首地址
public:
.....
}
可见,字符串类只定义了指针,并没有开辟具体的空间以存放字符串的内容,所以,无论是构造、析构还是加减等,均需要考虑动态开辟空间的问题,这也是字符串类的难点。
若不定义字符串的析构函数,则可以不定义它的拷贝的构造及赋值函数,若定义了析构函数,必须重新定义这两个成员函数。