参考书籍:primer C++
5.下标运算符
表示容器的类通常可以通过元素在容器中的位置访问元素,这些类一般会定义下标运算符operator[];
下标运算符必须是成员函数。下标运算符通常以所访问元素的引用作为返回值,这样做的好处是下标可以出现在赋值运算符的任意一端。如果一个类包含下标运算符,则它通常会定义两个版本,一个返回普通引用,一个是类的常量成员且返回常量引用。
例:
class StrVec {
public:
std::string& operator[] (std::size_t n)
{
return elements[n];
}
const std::string& operator[] (std::size_t n) const
{
return elements[n];
}
private:
std::string *elements; //指向数组首元素的指针
};
//假设svec是一个StrVec的对象
const StrVec cvec = svec; //把svec的元素拷贝到cvec中
//如果svec中含有元素,对第一个元素进行string的empty函数
if (svec.size() && svec[0].empty))
{
svec[0] = "zero"; //正确:下标运算符返回string的引用
cvec[0] = "Zip"; //错误:对cvec取下标返回的是常量引用
}
6.递增和递减运算符
定义递增和递减运算符的类应该同时定义前置版本和后置版本,这些运算符通常应该被定义为类的成员。
定义前置递增/递减运算符
在StrBlobPtr类中定义它们:
class StrBlobPtr{
public:
//递增和递减运算符
StrBlobPtr& operator++();
StrBlobPtr& operator--();
};
其工作机理为:首先调用check函数检验当前类是否有效,如果是,接着检查给定的索引值是否有效。如果check函数没有抛出异常,则运算符返回对象的引用。
// 前置版本:返回递增/递减对象的引用
StrBlobPtr& StrBlobPtr::operator++()
{
//如果curr已经指向了容器的尾后位置,则无法递增它
check (curr,"increment past end of StrBlobPtr");
++curr; //将curr在当前状态下向前移动一个元素
return *this; //返回递增后对象的引用
}
递减同理。
区分前置和后置运算符
后置版本接受一个额外的(不被使用的)int类型的形参,且后置运算符返回对象的原值(递增/递减之前的值),返回的形式是一个值而非一个引用。
同样在StrBlobPtr中添加后置运算符:
class StrBlobPtr {
public:
StrBlobPtr operator++(int);
StrBlobPtr operator--(int);
};
StrBlobPtr StrBlobPtr::operator++(int)
{
//此处无须检查有效性,调用前置递增运算时才需要检查
StrBlobPtr ret = *this; //记录当前值
++*this; //向前移动一个元素,前置++需要检查递增的有效性
return ret; //返回之前记录的状态
}
StrBlobPtr StrBlobPtr::operator--(int)
{
//此处无须检查有效性,调用前置递减运算时才需要检查
StrBlobPtr ret = *this; //记录当前值
--*this; //向后移动一个元素,前置--需要检查递减的有效性
return ret; //返回之前记录的状态
}
7.成员访问运算符
箭头运算符必须是类的成员函数,解引用运算符一般来说也是,但不
class StrBlobPtr {
public:
std::string& operator*() const
{
auto p = check(curr, "dereference past end");
return (*p) [curr]; //(*p)是对象所指的vector
}
std::string* operator->() const
{
//将实际工作委托给解引用运算符
return & this->operator* ();
}
}
运算符的用法与指针或者vector迭代器的对应操作完全一致:
StrBlob a1 = {“hi”,”bye”,”now”};
StrBlobPtr p(a1); //p指向a1中的vector
*p = “okay”; //将a1的首元素赋值
cout << p->size() <