运算符重载使自定义类的对象能像内置类型的变量一样使用内置的运算符,扩充运算符的功能,增强了C++ 语言的可扩充性。
注意点:
- 不是所有的运算符都能重载
- 重载不能改变运算符的优先级和结合性
- 重载不能改变运算符的操作数个数
- 不能创建新的运算符
运算符重载的方法
C++中规定,重载函数名为operator@
,其中,@
为要重载的运算符。如要重载+
运算符,该重载函数名为operator+
。
函数原型
运算符的重载不能改变运算符的运算对象数。因此,重载函数的形式参数个数(包括成员函数的隐式指针this)与运算符的运算对象数相同 。
运算符重载可以重载成成员函数也可以重载成全局函数实现。重载成全局函数时,最好把此函数设为友员函数。
如果作为类的成员函数,它的形式参数个数比运算符的运算对象数少1。这是因为成员函数有一个隐含的参数this。在C++中,把隐含参数this作为运算符的第一个参数。
所以当把一个一元运算符重载成成员函数时,该函数没有形式参数。把一个二元运算符重载成成员函数时,该函数只有一个形式参数,就是右操作数,当前对象是左操作数。
全局函数 vs成员函数
大多数运算符都可以重载成成员函数或全局函数。
赋值=
、下标[]
、函数调用()
和成员访问->
必须重载成成员函数。
具有赋值意义的运算符,如复合的赋值运算符以及++和--,不一定非要定义为成员函数,但最好定义为成员函数。
具有两个运算对象的运算符最好重载为全局函数,这样可以使得应用更加灵活。如果把加运算定义成全局函数,r是有理数类的对象,则2+r是一个合法的表达式。
特殊的运算符的重载
赋值运算符
一般情况下,缺省的赋值运算符重载函数能满足用户的需求。但是,当类含有类型为指针的数据成员时,可能会带来一些麻烦。
赋值运算符只能重载成成员函数,函数原型为:
class &class::operator=(const class &tight)
{...}
一般来讲,需要拷贝构造函数的类也需要重载赋值运算符。定义对象时给对象赋初值调用的是拷贝构造函数,程序的语句部分中的赋值语句调用的是赋值运算符重载函数。
下标运算符
下标运算符是二元运算符,第一个运算数是数组名,第二个运算数是下标值,下标运算符必须重载成成员函数。
函数原型为:
class & class::operator[](int index)
{...}
函数调用运算符
函数调用运算符()
是一个二元运算符。它的第一个运算对象是函数名,第二个参数是形式参数表。运算的结果是函数的返回值。
一个类重载了函数调用运算符,就可以把这个类的对象当做函数来使用
函数调用运算符必须重载成成员函数,函数原型为:
函数的返回值 operator() (形式参数表);
++和—运算符的重载
++
、--
是一元操作符,这两个操作符可以是前缀,也可以是后缀。而且前缀和后缀的含义是有区别的。所以,必须有两个重载函数。
但两个重载函数有相同的原型,区分方法在于:前缀:一元操作符。后缀:二元操作符。
作为成员函数重载:
++ob重载为:class &class::operator++()
ob-- 重载为:class class::operator--(int)
作为友元函数重载:
++ob重载为:operator++(class &ob)
ob--重载为:operator--(class &ob,int)
int
没有值的意义,仅仅起到区分作用,调用时,参数int一般传递给值0。
输入输出运算符重载
借助于流插入运算符>>
和流提取运算符<<
输入和输出用户自定义类的对象。
输入输出运算符必须被重载成全局函数
输入输出运算符是二元运算符,返回的是第一个对象的引用。由于第一个参数是输入输出流对象,只能重载为友元函数。
输出运算符的重载
ostream & operator<<(ostream & os, const class &obj)
{
os<< ....;
return os;
}
输入输出运算符重载
istream & operator>>(istream & is, class &obj)
{
is >> ....;
return is;
}
重载函数的原型设计考虑
参数设计
对于任何函数的参数,如果仅需要从参数中读,而不改变它,一般用const引用来传递。
只有会修改左值参数的运算符,如赋值运算符,左值参数不是常量,所以用地址传递。
返回值的类型设计
运算符的结果产生一个新值,就需要产生一个作为返回值的新对象。
对于逻辑运算符,人们希望至少得到一个int或bool的返回值。
所有的赋值运算符(如,=,+=等)均改变左值,应该能够返回一个刚刚改变了的左值的非常量引用。
自定义类型转换运算符
内置类型到类类型的转换
利用构造函数进行转换。例如,对于Rational类的对象r,可以执行r=2。 此时,编译器隐式地调用Rational的构造函数,传给它一个参数2。构造函数将构造出一个num=2,den= 1的Rational类的对象,并将它赋给r。
explicit构造函数
任何单参数的构造函数都可以被编译器用来执行隐式转换,即把内置类型转换成对应的类类型。
在某些情况下,隐式转换是不受欢迎的。
将单参数的构造函数定义为explicit,将告诉编译器不允许执行隐式转换。
如将Ratioanal类的构造函数定义成
explicit Rational(int n1 = 0, int n2 = 1)
则对于Rational类的对象r1和r2,执行r1 = 2 + r2;编译器就会报错。
类类型到其它类型的转换
可以通过类型转换函数实现,类型转换函数必须重载成成员函数。
类型转换函数的格式:
operator type() const
{
return (结果为目标类型的表达式);
}
类型转换函数的特点:无参数,无返回值,是const函数。
有了这个函数,我们可以将一个Rational类的对象r赋给一个type类型的变量x。如r的值为(1,3),经过赋值double x = r后,x的值为0.333333。