运算符重载: 使对象的运算运算表现得和编译器内置类型一样,使同一个运算符可以有不同的功能。即定义一个重载运算符的函数,使指定的运算符不仅能实现原有的功能,而且能实现在函数中指定的新的功能。
运算符重载实质上是函数的重载,重载运算符的函数一般格式如下:
函数类型 operator 运算符名称(形参表)
{对运算符的重载处理}
operator是关键字,是专门用于定义重载运算符的函数的,运算符名就是C++已有的运算符。
运算符重载的规则:
(1)C++不允许用户自己定义新的运算符,只能对已有的C++运算符进行重载。
(2)C++允许重载的运算符
不能重载重载的运算符只有5个:
① . 成员访问运算符
② * 成员指针访问运算符
③ :: 域运算符
④sizeof 长度运算符
⑤ ?: 条件运算符
(3)重载不能改变运算符运算对象(即操作数)的个数
(4)重载不能改变运算符的优先级别
(5)重载不能改变运算符的结核性
(6)重载不能改变默认的参数
(7)重载的运算符必须和用户自定义类型的对象一起使用,其参数至少有一个是类对象(或类对象的引用)
(8)用于类对象的运算符一般必须重载,但有两个例外,“=”和“&”不必用户重载
运算符重载的函数有两种处理方式:
1.把运算符重载的函数作为类的成员函数
如果运算符重载函数作为成员函数,它可以通过this指针自由地访问本类的数据成员,因此可以少写一个函数的参数。
2.运算符重载的函数不是类的成员函数(可以是一个普通函数),在类中把它声明为友元函数
将双目运算符重载为友元函数时,由于友元函数不是该类的成员函数,因此在函数的形参列表中必须有两个参数,不能省略。
注意:
1.运算符重载不仅可以给类的成员方法提供重载,还可以在全局提供重载。
2.编译器做对象运算的时候,会调用运算符的重载函数(优先调用成员方法);如果没有成员方法,就在全局作用域找合适的运算符重载函数。
为什么需要运算符重载?它有什么好处?我们来看一个简单的代码
template <typename T>
T sum(T a, T b)//a,b都没有指定的类型
{
return a + b;//a.operator+(b)
}
这里的a,b都没有指定类型,如果我们使用的是编译器的内置类型,可以使用;但是如果T类型如果是对象类型,a与b则是两个对象,对象与对象的相加就要用到我们运算符重载。那么如何进行重载呢,我们以复数类CComplex为例进行运算符的重载。
1.普通的复数类CCpmplex中加法运算符重载
①先来看普通的复数类的运算符重载
//复数类
class CComplex
{
public:
//CComplex() CComplex(20) CComplex(30,30)
CComplex(int r = 0, int i = 0)//构造
:mreal(r), mimage(i){}
//加法运算符重载
CComplex operator+(const CComplex &src)
{
CComplex comp;
comp.mreal = this->mreal + src.mreal;
comp.mimage = this->mimage + src.mimage;
return comp;
//return CComplex(this->mreal + src.mreal,
//this->mimage + src.mimage);
}
void show()
{
cout << "real:" << mreal << " image" << mimage << endl;
}
private:
int mreal;//实部
int mimage;//虚部
};
int main()
{
CComplex comp1(10, 10);
CComplex comp2(20, 20);
//comp1.operator.+(comp2)加法运算符的重载函数
CComplex comp3 = comp1 + comp2;
comp3.show();
CComplex comp4 = comp1 + 20;//将20加到comp1实部
comp4.show();
return 0;
}
输出结果:执行成功。
②那么我们输入如下代码,comp4可以执行成功吗?
CComplex comp4 = comp1 + 20;//将20加到comp1实部
输出结果:执行成功,comp4可以成功重载,comp4中comp1调用了中间加法运算符重载,将20当作实参传入:com1.operator+(20),形参类型为当前复数类型的引用变量,引用不了整数20。实参类型到形参类型强转,int->CComplex,CComplex(int);整型如何转为CComplex复数类下,编译器会找复数类型有没有带整型参数的构造函数来生成临时对象,成功找到。
③我们再来看看,comp5可以执行成功吗?
CComplex comp5 = 30 + comp1;
我们来分析一下:comp5不能重载成功。30并不需要类型转换,无法调用到成员方法中的运算符重载函数,所以执行失败。
那么如何解决这个问题呢?
我们在全局作用域再提供一个运算符重载函数:不需要对象来调用,除了运算符+,其他都当做实参传入。并在私有下声明其为友元函数。全局的运算符重载功能:1.对象与对象运算 2.对象与整数运算 3.整数与对象运算。
CComplex operator+(const CComplex &lhs, const CComplex &rhs)
{
return CComplex(lhs.mreal + rhs.mreal, lhs.mimage + rhs.mimage);
}
输出结果:执行成功。
有了全局的重载函数,局部的可省略掉:
//复数类
class CComplex
{
public:
CComplex(int r = 0, int i = 0)//构造
:mreal(r), mimage(i){}
void show()
{
cout << "real:" << mreal << " image" << mimage << endl;
}
private:
int mreal;//实部
int mimage;//虚部
friend CComplex operator+(const CComplex &lhs, const CComplex &rhs);//友元
};
CComplex operator+(const CComplex &lhs, const CComplex &rhs)
{
return CComplex(lhs.mreal + rhs.mreal, lhs.mimage + rhs.mimage);
}
④++运算符的重载:
因为我们++或–是单目运算符,operator++这样写无法区别前置++与后置++
因此,为了区分它们:
//复数类
class CComplex
{
public:
//CComplex() CComplex(20) CComplex(30,30)
CComplex(int r = 0, int i = 0)//构造
:mreal(r), mimage(i){}
CComplex operator++(int)//后置++重载
{
//CComplex comp = *this;//保留旧值
//mreal += 1;
//mimage += 1;
//return comp;//返回旧值
return CComplex(mreal++, mimage++);
}
CComplex& operator++()//前置++重载
{
mreal += 1;
mimage += 1;
return *this;//返回加完后的值
}
void show()
{
cout << "real:" << mreal << " image" << mimage << endl;
}
private:
int mreal;//实部
int mimage;//虚部
friend CComplex operator+(const CComplex &lhs, const CComplex &rhs);//友元
};
CComplex operator+(const CComplex &lhs, const CComplex &rhs)
{
return CComplex(lhs.mreal + rhs.mreal, lhs.mimage + rhs.mimage);
}
int main()
{
CComplex comp1(10, 10);
CComplex comp2(20, 20);
comp5 = comp1++;
comp1.show();
comp5.show();
comp5 = ++comp1;
comp1.show();
comp5.show();
return 0;
}
输出结果:执行成功。
⑤复合运算符赋值重载:例如+=即为复合赋值运算符
还是接着CComplex来看:
//+=运算符重载
void operator+=(const CComplex &src)
{
mreal += src.mreal;
mimage += src.mimage;
}
执行结果:执行成功。
假如我们写了一个模板代码:里面接收一个对象或者变量,在show中进行打印。但是存在问题,并不知道用户将来会有什么类型为其实例化。万一用户使用自定义的类类型对其实例化,例如:CComplex,如何对CComplex对象打印?
template<typename T>
void show(T a)
{
cout << a <<endl;
}
因此为了让对象的打印达到通用,我们需要对输出运算符进行重载,需要将其提供为全局方法并将其声明为友元函数。
ostream& operator<<(ostream &out, const CComplex &src)
{
out << "mreal:" << src.mreal << "mimage:" << src.mimage << endl;
return out;
}
执行结果:输出成功。
输入运算符重载:并且类内声明其为友元。
istream& operator>>(istream &in, CComplex &src)
{
in >> src.mreal >> src.mimage;
return in;
}