<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"><span style="font-size:24px;">操作符重载问题</span></span>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"><span style="font-size:24px;">操作符重载问题</span></span>
1.为什么要使用重载操作符呢?
因为普通操作符只能识别内置类型,而对于自定义的类它并不能识别,我们也可以通过重新写一个函数来实现这些操作符的功能,但是这样的做法太麻烦,且代码可读性低.而使用重载操作符,可以令程序更自然、更直观,而滥用操作符重载会使得类难以理解,在实践中很少发生明显的操作符重载滥用。但有些程序员会定义operator+来执行减法操作,当一个重载操作符不明确时,给操作符取一个名字更好,对于很少用的操作,使用命名函数通常比用操作符好,如果不是普通操作,没有必要为简洁而用操作符。
2.操作符重载概念:
重载操作符是具有特殊函数名的函数,关键字operator后面接需要定义的操作符符号。
操作符重载也是一个函数,具有返回值和形参表。它的形参数目与操作符的操作数目相同,
函数调用操作符可以接受任意数目的操作数。
操作符重载也是一个函数,具有返回值和形参表。它的形参数目与操作符的操作数目相同,
函数调用操作符可以接受任意数目的操作数。
3.不能被重载的操作符
操作符重载时注意的问题
1、不能通过连接其他符号来创建新的操作符:比如operator@;
void operator @(){}
2、重载操作符必须有一个类类型或者枚举类型的操作数
void operator @(){}
2、重载操作符必须有一个类类型或者枚举类型的操作数
全部为内置类型时就不用重载
3、用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不能改变其含义
5、不在具备短求职特性
重载操作符不能保证操作符的求职顺序,在重载&&和||中,对每个操作数
都要进行求值,而且对操作数的求职顺序不能做规定,因此:重载&&、
||和逗号操作符不是好的做法。
6、作为类成员的重载函数,其形参看起来比操作数数目少1
成员函数的操作符有一个默认的形参this,限定为第一个形参。
7、一般将算术操作符定义为非成员函数,将赋值运算符定义成员函数
int operator +(const int _iNum1, const int _iNum2) // 报错
{
return (_iNum1 + _iNum2);
}
typedef enum TEST {one ,two ,three };
int operator+(const int _iNum1 , const TEST _test )
{
return _iNum1;
}
3、用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不能改变其含义
5、不在具备短求职特性
重载操作符不能保证操作符的求职顺序,在重载&&和||中,对每个操作数
都要进行求值,而且对操作数的求职顺序不能做规定,因此:重载&&、
||和逗号操作符不是好的做法。
6、作为类成员的重载函数,其形参看起来比操作数数目少1
成员函数的操作符有一个默认的形参this,限定为第一个形参。
CTest operator+(const CTest test1, const CTest test2)const // 报错
{
return test1;
}
CTest operator+(const CTest test1)const
{
return test1;
}
7、一般将算术操作符定义为非成员函数,将赋值运算符定义成员函数
以复数为例,它的代码实现如下:
Complex &opreator=(const Complex &c)
{
if(this!=&c)
{
real=c.real;
image=c.image;
}
return *this;
}
在上述这个简单的代码中,需要注意四个问题
1)如果将Complex改写为void,则它没有返回值就无法满足二次赋值
2且如果以值得形式返回,它会生成临时变量,但他的生命周期短于函数,所以要返回引用
3)有可能还存在这种形式 date此时是一个类,在不注意的时候有可能会用它自身给自身赋值,因此在进入函数之后要对this这个隐性参数进行检测,因此传参的时候要传地址
date a;
date &c=a;
date &b=a;
c=b;
4)最后return的也是引用,即是地址
因为如果不将其定义为友元,它无法访问类的私有成员
9、== 和 != 操作符一般要成对重载
10、下标操作符[]:一个非const成员并返回引用,一个是const成员并返回引用
9、== 和 != 操作符一般要成对重载
10、下标操作符[]:一个非const成员并返回引用,一个是const成员并返回引用
代码实现:
DataType &operator[](size_t index)
{
if (index >= size)
{
assert(false);
}
return this->pData[index];
}
分析上述代码:
之所以要返回引用,是因为数组下标即可作左值,也可作右值。
当它返回临时变量的时候不能做左值,因为临海四变量具有常性,不能被修改
11、解引用操作符*和->操作符,不显示任何参数
13、自增自减操作符
前置式++/--必须返回被增量或者减量的引用
11、解引用操作符*和->操作符,不显示任何参数
13、自增自减操作符
前置式++/--必须返回被增量或者减量的引用
代码实现如下:
后缀式操作符必须返回旧值,并且应该是值返回而不是引用返回,且多了一个参数
Complex &operator++()
{
_real++;
return *this;
}
后缀式操作符必须返回旧值,并且应该是值返回而不是引用返回,且多了一个参数
代码实现:
Complex operator++(int)
{
Complex tmp=(*this);
_real++;
return tmp;
}
此处不能返回引用时因为tmp是局部变量,它的生命周期短于函数,函数运行结束他已经不存在了
14、输入操作符>>和输出操作符<<必须定义为类的友元函数
在类内用friend进行声明
ostream& operator<<(ostream& out, const Arr&a)
{
out << a.size;
out << a.capacity;
return out;
}
istream& operator>>(istream& in, Arr&a)
{
in >> a.size;
in >> a.capacity;
return in;
}
分析上述代码:输出为例
如果将其放在类内,它是类的成员函数,则它的调用必须通过一个类对象来启动它
eg:a<<cout 而用这个格式就无法进行连续的输出所以将其定义为类的友元函数
例如要连续输出时cout<<a<<a<<endl;
当返回值不写成引用时,他会在第一次cout之后返回一个临时变量,临时变量具有常性,它不能修改cout的状态
cout在运行的时候一直在改变,cout其实是ostream流中一个对象的值
在参数上,由于流在输出的时候他的对象会发生变化,所以不加const,而对于类对象a进行输出,所以要加const,防止不小心将其改变