一、讲在前面
大家好,我讲一下运算符的重载
二、关于友元的补充
在讲运算符重载之前,先补充一下友元的知识
类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为他的友元(friend)。如果类想把一个函数作为他的友元,只需要增加一条以friend关键字开始的函数声明语句即可
友元声明只能出现在类定义的内部,但是在类内出现的具体位置不限,不是类的成员也不受他所在的区域访问控制级别的约束。
一般来说,最好在类定义开始或者结束前的位置集中声明友元
友元的声明仅仅指定了访问的权限,而非一个通常意义上的函数的声明。如果我们希望类的用户能够调用某个友元函数,那么我们必须在友元声明之外,再专门对函数进行一次声明。
友元与普通函数的区别在于,友元可以直接访问类的私有成员,而普通函数只能通过类的公有成员访问其私有成员
三、运算符重载
1. 基本概念的碎碎念
对于一个运算符函数来说,它或者是类的成员,或者至少含有一个类类型的参数
我们可以重载大多数(但不是全部)运算符,只能重载已有的运算符,而无权发明新的运算符
当一个重载的运算符是成员函数时,this绑定到左侧运算对象。成员运算符函数的(显式)参数数量比运算对象的数量少一个
2. 调用一个重载的运算符函数
a @ b;
a.operator@(b);
operator@(a,b);
Line1 是隐式调用形式
Line2、3是显式调用形式
2-1. 关于Line2、3
第一种形式是@被重载为类的非静态成员函数的解释方式,这种方式要求运算符@左边的参数必须是一个对象,operator@是该对象的成员函数
第二种形式是@作为类的友元或普通重载函数时的解释方式
2-2. 二元运算符作为类的非静态成员函数、普通函数、类的友元函数重载的区别如下
以非静态成员函数的方式重载二元运算符时,只能够有一个参数,它实际上是函数的第二个参数(即运算符右边的操作数)。其第一个参数(运算符左边的操作数)由c++通过this指针隐式传递,而作为普通函数和类的友元函数重载时需要两个参数。
调用类的重载运算符时,作为类成员函数运算符的左参数必须是一个类对象,而作为友元或普通函数重载的运算符则无此限制。
2-2-1. 重载二元运算符为非静态成员函数的形式如下:
class X{
……
T1 operator@(T2 b){ //实际为T1 operator@(X *this , T2 b)
……
} //其中X *this形参由编译器自动添加
};
其中,T1是运算符函数的返回类型,T2是参数的类型,原则上T1、T2可以是任何数据类型,而实际上它们常与X相同
2-2-2. 重载二元运算符为类的友元函数或普通函数时需要两个参数,其形式如下:
class X{
……
friend T1 operator@(T2 b,T3 b);
};
T1 operator(T2 a,T3 b){……} //友元函数定义
T1 operator(T2 a,T3 b){……} //普通函数
T1、T2、T3可以是不同的数据类型,但他们通常都是类X。
友元与一般函数重载的区别是:友元可以访问类的任何数据成员,而普通函数只能访问类的public成员
3. 重载运算符类型
(这里不全说了,只说一下Jerry今天委托我讲的几个吧)
3-1. 输入输出运算符
3-1-1. 输入运算符
这里直接给出固定格式
ostream &operator << (ostream &os , const T1 a)
{
os<<……;
return os
}
通常,输出运算符应该主要负责打印对象的内容而非控制格式,输出运算符不应该打印换行符
3-1-2. 输入运算符
这里先给出代码
istream &operator >> (istream &is , const T1 & a)
{
is>>a.xxx……;
return is;
}
3-1-3. 需要注意的问题
输入输出运算符必须是非成员函数
输入运算符必须处理输入可能失败的情况(如果输入失败,就将给定的对象默认为空或者其他,这样可以保证对象处于正确的状态),而输出运算符不需要
4. 算数和关系运算符
4-1. 关系运算符
一般来说这个重载后是判断是否相等或者其他关系的,所以返回值是true或false
所以一般用bool型来定义
4-2. 递增和递减运算符
在迭代器类中通常会实现递增运算符( ++ )和递减运算符( – ),这两种运算符使得类可以在元素的序列中前后移动。C++语言并不要求递增和递减运算符必须是类的成员。但是因为他们改变的正好是所操作对象的状态,所以建议将其设定为成员函数。
对于内置类型来说,递增和递减运算符具有前置版本也有后置版本,同样我们也应该为类定义两个版本的递增和递减运算符,接下来我们首先介绍前置版本。然后实现后置版本。
4-2-1. 定义前置递增/递减运算符
举例说明
class Point{
public:
//递增和递减运算符
Point& operator++(); //前置运算符
Point& operator--();
}
Point& Point :: operator++()
{
x++;
return *this;
}
为了与内置版本保持一致,前置运算符应该返回递增或递减后对象的引用
4-2-2. 区分前置和后置运算符
由于普通的重载形式无法区分这两种情况。前置版本和后置版本用的是同一个符号,意味着其重载版本所用的名字将是相同的,并且运算对象的数量和类型也相同。
为了解决这一问题,后置版本要接受一个额外的(不被使用)int类型的形参。当我们使用后置运算符时,编译器为这个形参提供一个值为0的实参。
举例说明
class Point{
public:
//递增和递减运算符
Point operator++(int); //后置运算符
Point operator--(int);
//其他成员跟之前的版本一致
}
为了与内置版本保持一致,后置运算符应该返回对象的原值(递增或递减之前的值),返回的形式是一个值而非引用
对于后置版本来说,在递增对象之前需要首先记录对象的状态:
Point Point :: operator++(int)
{
Point ret = *this;
++*this;
return ret;
}
因为我们无需用到int形参,所以勿需为其命名
四、写在最后
看完这篇文章,其实运算符重载的事就学的差不多了,关于+ 、 - 、 * 、 / 的运算符重载,其实是差不多的,在这就没有多家赘述。虽然写的不是很好,但是强烈建议把本篇文章放进收藏夹吃灰
好吧,还是CSDN写文章更香