前言
本篇继续C++类的进阶学习,运算符重载。
运算符重载与函数重载
C++中,函数重载使同一函数名根据不同的参数列表实现不同的功能。
运算符重载与函数重载类似,使同一运算符对于不同类型的操作数实现特定的功能。
运算符重载实际上也是由函数重载实现的,本质上是函数重载。
运算符重载
关键字operator
用于重载运算符。
运算符重载格式为:返回类型 operator 运算符名称 (参数列表) {}
。
注意:重载运算符的参数列表中,必须有一个参数类型是自定义类或者结构或者枚举类型。
运算符重载,类方法
类内重载的运算符将成为类方法。
下面是一个重载+
实现两数组相加的例子:
#include <cstdio>
#include <iostream>
class easyClass{
private:
int arr[5] {1,2,3,4,5};
public:
void operator+ (const easyClass);
};
void easyClass::operator+ (const easyClass ec){
for(int i=0; i<5; i++)
{
arr[i] += ec.arr[i];
std::cout << arr[i] <<std::endl;
}
}
int main()
{
easyClass a;
easyClass b;
a + b;
return 1;
}
类easyClass
内重载了加法运算符+
,是从左向右结合的运算符。在编译时,+
左侧的操作数是一个类的对象,因此将使用类内重载的+
:
a.operator+(b);
也就是说,对于类内运算符重载,a + b
实际上是对象a
调用类方法+
,对象b
作为参数。
void easyClass::operator+ (const easyClass)
的参数列表隐藏了调用对象自身,实际上是通过this
指针传递了调用对象。
运算符重载需要遵循以下规则:
- 必须有一个参数类型为自定义类,结果或者枚举;
- 不能重载不存在的运算符;
- 不能改变运算符本身的操作方式,操作数,结合顺序,优先级;
- 部分运算符不能重载,包括
sizeof()
,.
,::
,?:
等;
运算符重载,友元
类内重载运算符使其成为了类方法。但是,这里会出现一个操作数顺序问题,如下所示:
class A{
private:
int a_ {1};
public:
int operator* (int);
};
int A::operator* (int x){
return a_ + x;
}
void main(){
A a;
a * 2;
2 * a; // error!
}
类A
重载了*
,因此a*2
将被转换为a.operator*(2)
。
但2*a
将报error: no match for 'operator*' (operand types are 'int' and 'A')
,因为类方法必须通过对象才能调用。
为了解决上面这个顺序问题,重载的运算符不能是类方法,但又要能够访问类的私有成员。因此,可以定义友元重载运算符,如下所示:
class A{
private:
int a_ {1};
public:
int operator* (int);
friend int operator* (int, A);
};
int A::operator* (int x){
return a_ * x;
}
int operator* (int x, A a){
return a.a_ * x;
}
void main(){
A a;
a * 2;
2 * a;
}
编译器将2 * a
替换为operator*(2, a)
。
注意:在类内通过friend
关键字声明重载的运算符是友元类型,在类外定义时不能使用friend
关键字。
注意:重载运算符时,必须注意类方法与友元函数可能出现的二义性问题,例如形参为对象时,就可能发生冲突:
class A{
private:
int a_ {1};
public:
int operator* (A); // redefinition
friend int operator* (A, A); // redefinition
};
另外,如果已经在类内重载了运算符,则不需要友元函数也能够解决操作数顺序问题,示例如下:
class A{
private:
int a_ {1};
public:
int operator* (int);
};
int A::operator* (int x){
return a_ * x;
}
int operator* (int x, A a){
return a * x;
}
void main(){
A a;
a * 2;
2 * a;
}
由于类方法限定了重载运算符只能由对象调用,即首操作数必须为对象,那么在全局再重载一次该运算符,该运算符内正确调用类内重载的运算符即可。
<< 运算符的重载
为一个类重载<<
运算符,更便于cout
输出。
通常,重载<<
应当使用友元函数,因为<<
的调用对象通常是ostream
类型的cout
对象;此外,返回类型也应当是ostream &
,这样能够连续输出流:
class A{
private:
int a_ {1};
public:
friend std::ostream& operator<< (std::ostream& os, const A&);
};
std::ostream& operator<< (std::ostream& os, const A& a) {
os << a.a_ << " OK!" << std::endl;
return os;
}
int main(){
A a;
std::cout << a;
return 1;
}
后记
本篇学习了运算符重载,下一篇将继续类的进阶学习,类的自动转换和强制转换。