【C++基础】系列博客为参考《C++ Primer中文版(第5版)》(C++11标准)一书,自己所做的读书笔记。
本文为原创文章,未经本人允许,禁止转载。转载请注明出处。
1.递增和递减运算符
在迭代器类中通常会实现递增运算符(++
)和递减运算符(--
),这两种运算符使得类可以在元素的序列中前后移动。C++语言并不要求递增和递减运算符必须是类的成员,但是因为它们改变的正好是所操作对象的状态,所以建议将其设定为成员函数。
对于内置类型来说,递增和递减运算符既有前置版本也有后置版本。同样,我们也应该为类定义两个版本的递增和递减运算符。
1.1.定义前置递增/递减运算符
我们在StrBlobPtr类中定义递增和递减运算符:
1
2
3
4
5
6
7
class StrBlobPtr {
public:
//递增和递减运算符
StrBlobPtr& operator++(); //前置运算符
StrBlobPtr& operator--();
//其他成员和之前的版本一致
};
为了与内置版本保持一致,前置运算符应该返回递增或递减后对象的引用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//前置版本:返回递增/递减对象的引用
StrBlobPtr& StrBlobPtr::operator++()
{
//如果curr已经指向了容器的尾后位置,则无法递增它
check(curr, "increment past end of StrBlobPtr");
++curr; //将curr在当前状态下向前移动一个元素
return *this;
}
StrBlobPtr& StrBlobPtr::operator--()
{
//如果curr是0,则继续递减它将产生一个无效下标
--curr; //将curr在当前状态下向后移动一个元素
check(curr, "decrement past begin of StrBlobPtr");
return *this;
}
1.2.区分前置和后置运算符
要想同时定义前置和后置运算符,必须首先解决一个问题,即普通的重载形式无法区分这两种情况。前置和后置版本使用的是同一个符号,意味着其重载版本所用的名字将是相同的,并且运算对象的数量和类型也相同。
为了解决这个问题,后置版本接受一个额外的(不被使用)int类型的形参。当我们使用后置运算符时,编译器为这个形参提供一个值为0的实参。尽管从语法上来说后置函数可以使用这个额外的形参,但是在实际过程中通常不会这么做。这个形参的唯一作用就是区分前置版本和后置版本的函数,而不是真的要在实现后置版本时参与运算。
接下来我们为StrBlobPtr添加后置运算符:
1
2
3
4
5
6
7
class StrBlobPtr {
public:
//递增和递减运算符
StrBlobPtr operator++(int); //后置运算符
StrBlobPtr operator--(int);
//其他成员和之前的版本一致
};
为了与内置版本保持一致,后置运算符应该返回对象的原值(递增或递减之前的值),返回的形式是一个值而非引用。
对于后置版本来说,在递增对象之前需要首先记录对象的状态:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//后置版本:递增/递减对象的值但是返回原值
StrBlobPtr StrBlobPtr::operator++(int)
{
//此处无须检查有效性,调用前置递增运算时才需要检查
StrBlobPtr ret = *this; //记录当前的值
++*this; //向前移动一个元素,前置++需要检查递增的有效性
return ret; //返回之前记录的状态
}
StrBlobPtr StrBlobPtr::operator--(int)
{
//此处无须检查有效性,调用前置递减运算时才需要检查
StrBlobPtr ret = *this; //记录当前的值
--*this; //向后移动一个元素,前置--需要检查递减的有效性
return ret; //返回之前记录的状态
}
因为我们不会用到int形参,所以无须为其命名。
1.3.显式地调用后置运算符
如此处介绍的,可以显式地调用一个重载的运算符,其效果与在表达式中以运算符号的形式使用它完全一样。如果我们想通过函数调用的方式调用后置版本,则必须为它的整型参数传递一个值:
1
2
3
StrBlobPtr p(a1); //p指向a1中的vector
p.operator++(0); //调用后置版本的operator++
p.operator++(); //调用前置版本的operator++
尽管传入的值通常会被运算符函数忽略,但却必不可少,因为编译器只有通过它才能知道应该使用后置版本。