基本概念
重载的运算符是具有特殊名字的函数:它们的名字由关键字 operator 和其后要定义的运算符号共同组成,如果一个运算符是成员函数,则它的第一个运算对象绑定到隐式的 this 指针上,因此成员运算符函数的显式参数数量比运算对象总数少一个。
当把运算符定义成成员函数时,它的左侧运算对象必须是运算符所属类的一个对象,如果我们像提供含有类对象的混合类型表达式(类似 int + double),则运算符必须定义成非成员函数。
输入和输出运算符
输入输出运算符必须是非成员函数
ostream &operator<<(ostream &os, const Sales_data &item)
{
os << item.isbn() << " " << item.units_sold << " "
<< item.revenue << " " << item.avg_price();
return os;
}
算术和关系运算符
因为这些运算符一般不需要改变运算对象的形态,所以形参都是常量的引用。
Sales_data
operator+(const Sales_data &lhs, const Sales_data &rhs)
{
Sales_data sum = lhs;
sum += rhs;
return sum;
}
赋值运算符
与拷贝赋值及移动赋值运算符一样,其他重载的赋值运算符也必须先释放当前内存空间,再创建一片新空间
class StrVec {
public:
StrVec &operator=(std::initializer_list<std::string>);
};
StrVec &StrVec::operator=(initializer_list<string> il)
{
// alloc_n_copy 分配内存空间并从给定范围内拷贝元素
auto data = alloc_n_copy(il.begin(), il.end());
free();
elements = data.first;
first_tree = cap = data.second;
return *this;
}
递增和递减运算符
为了与内置版本保持一致,后置运算符应该返回对象的原值,返回的形式是一个值而非引用
class StrBlobPtr {
public:
StrBlobPtr& operator++(); // 前置运算符
StrBlobPtr operator++(int); // 后置运算符
};
StrBlobPtr& StrBlobPtr::operator++()
{
// 如果指针已经指向了容器的尾后位置,则无法递增它
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
StrBlobPtr StrBlobPtr::operator++(int)
{
// 此处无须检查有效性,调用前置递增运算时才需要检查
StrBlobPtr ret = *this;
++*this;
return ret;
}
成员访问运算符
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*();
}
};
我们能令 operator* 完成任何我们指定的操作,但箭头运算符永远不能丢掉成员访问这个最基本的含义。
对于形如 point -> mem 的表达式,point 必须是指向类对象的指针或者是一个重载了 operator-> 的类的对象。根据 point 类型的不同,point -> mem 分别等价于
(*point).mem; // point 是一个内置的指针类型
point.operator()->mem; // point 是类的一个对象
函数调用运算符
如果类重载了函数调用运算符,则我们可以像使用函数一样使用使用该类的对象。
struct absInt {
int operator()(int val) const {
return val < 0 ? -val : val;
}
};