文章目录
重载运算符
1、基本概念
1.1、定义
重载的运算符是具有特殊名字的函数:它们的名字由关键字operator和其后要定义的运算符组成。重载的运算符也包含返回类型,参数列表以及函数体。
istream& operator>>(istream& in,const T name){
........//具体实现
return in;
}
1.2、重载运算符函数参数
重载运算符函数的参数数量与该运算符作用的运算对象数量一样多。
一元运算符有一个参数。
一元运算符 | 备注 |
---|---|
+ | 正号,表示正数 |
- | 负号,表示负数 |
* | 解引用 |
& | 取地址 |
二元运算符有两个参数。对于二元运算符来说,左侧运算对象传递给第一个参数,而右侧运算对象传递给第二个参数。
//v1,v2是同一个类的对象。
v1=v2; //=是二元运算符,v1是左侧参数,v2是右侧参数。
若二元运算符函数是成员函数(在类里面),那么参数会减少一个,this会绑定到左侧运算对象。
1.3、特殊情况
当运算符作用于内置类型的运算对象时,我们无法改变运算符的含义。
int operator+(int,int); //错误,作用于内置对象
//可以把int换成自己定义的类类型(把int全部替换)
1.4、特殊情况(1)
从参数的数量判断是二元还是一元运算符。
一/二元运算符 | 备注 |
---|---|
+ | 正号,表示正数;加号(二元); |
- | 负号,表示负数;减号(二元); |
* | 解引用;乘号(二元); |
& | 取地址;并(二元) |
1.5、重载不改变的性质
重载运算符,不改变运算符的优先级和结合律。不能发明新的运算符号。
1.6、这些运算符不能重载
不能重载的运算符 | |
---|---|
:: | |
.* | |
. | |
? : |
1.7、运算符重载函数调用的方式:
//date,date2是一个类的对象。
date1+date2; //普通的表达式
operator+(date1,date2); //等价的函数调用,不是成员函数
date1.operator(date2); //operator+是类的成员函数。
1.8、作为成员函数或者非成员的选择
- 赋值(=),下标([ ]),调用(())和成员访问箭头(->)必须为成员函数。
- 复合运算符一般来说应该是成员函数。(==,+=,-=,*=等等)。
- 改变对象状态的运算符或者与给定类型密切相关的运算符通常为成员。例如,递增,递减,取地址。
- 具有对称性的运算符可能转换任意一端的运算对象,应该是非成员函数。例如算术,相等性,关系和位运算符等。
2、重载操作
2.1、重载输出运算符<<
ostream& operator<<(ostream& os,const T&,name)
{
.......//具体操作
return os;
}
- 返回引用,目的是为了可以连续输出,例如:cout<<i<<endl;
也是与内置版本实现功能相同。 - 第二个形参的引用的原因:避免复制实参。
- 第二个形参是const的原因,我们只是输出,不需要对数据进行修改。
- <<必须定义为非成员函数,通常定义为友元函数。
2.2、重载输入运算符>>
istream& operator>>(istream& is,T& name)
{
.......//具体操作
if(is) //检查是否输入成功
{}
else //输入失败的情况
{}
return is;
}
- 当读取操作发生错误时,输入运算符应该负责从错误中恢复。
- 输入的数据中,其中一个输入错误,就会导致读取错误。
- 只有数据全部正确后,才真正读入,否则进入else{}里面。
-
>>必须定义为非成员函数,通常定义为友元函数。
2.3、算术和关系运算符
T operator+(const T& name1,const T& name2)
{
......//具体操作
}
- 通常定义为非成员函数。
- 如果同时定义了算术运算符和相关的复合运算符,通常情况下应该使用复合赋值来实现算术运算符。
2.4、相等运算符
bool operator==(const T& name1,const T& name2)
{
//判断对应的每个数据成员是否全都相同。
}
bool operator!=(const T& name1,const T& name2)
{
return !(name1==name2);
}
体现出来的设计准则:
- 定义成类成员函数。
- operator=,应该能判断数据是否重复。
- 通常情况下,相等运算符还具有传递性。
- 定义了==,同时应该也定义!=。
- 只具体写其中一种(==/!=),另外一种对详细的取反。例如上面的代码。
2.5、赋值运算符
T& operator=(T& name1)
{
......//具体操作。使用this
}
- 必须定义为成员函数。
- 必须返回引用类型。
2.6、下标运算符
T& operator[](auto n)
{
return elements[n];
}
const T&operator[](auto n) const
{
return elements[n];
}
- 必须为成员函数。
- 下标运算符返回一个引用变量。
- 最好同时定义下标运算符的常量版本和非常量版本。防止出现常量时,无法匹配。
2.7、递增/递减运算符
- 应同时定义前置版本和后置版本。
- 前置应该返回引用。原因:与内置版本保持一致。
- 前置与后置的区别是:后置接受一个int类型的参数,这个参数只是为了区分前置和后置。
class StrBlobptr{
//前置
StrBlobptr& operator++();
StrBlobptr& operator--();
//后置
StrBlobptr operator++(int);
StrBlobptr operator--(int);
}
//前置
StrBlobptr& StrBlobptr::operator++()
{
//先检查是否在最后一个,再加一个,就会超出范围。
......
//检查成功后,再加一。
++elem;
return *this;
}
//后置
StrBlobptr StrBlobptr::operator++(int)
{
StrBlobptr ret=*this;
++*this; //调用前置++,会检查是否超出范围
return ret;
}
2.8、成员访问运算符
T& operator*()const
{
}
T* operator->() const
{
}
- 必须的类成员函数。
2.9、函数调用运算符
int operator()(int val)const
{
//具体操作
}
- 必须定义为成员函数。