基本概念

重载的运算符是具有特殊名字的函数:它们的名字由关键字 operator 和其后要定义的运算符号共同组成,如果一个运算符是成员函数,则它的第一个运算对象绑定到隐式的 this 指针上,因此成员运算符函数的显式参数数量比运算对象总数少一个。

当把运算符定义成成员函数时,它的左侧运算对象必须是运算符所属类的一个对象,如果我们像提供含有类对象的混合类型表达式(类似 int + double),则运算符必须定义成非成员函数。

输入和输出运算符

输入输出运算符必须是非成员函数

  1. ostream &operator<<(ostream &os, const Sales_data &item)
  2. {
  3. os << item.isbn() << " " << item.units_sold << " "
  4. << item.revenue << " " << item.avg_price();
  5. return os;
  6. }

算术和关系运算符

因为这些运算符一般不需要改变运算对象的形态,所以形参都是常量的引用。

  1. Sales_data
  2. operator+(const Sales_data &lhs, const Sales_data &rhs)
  3. {
  4. Sales_data sum = lhs;
  5. sum += rhs;
  6. return sum;
  7. }

赋值运算符

与拷贝赋值及移动赋值运算符一样,其他重载的赋值运算符也必须先释放当前内存空间,再创建一片新空间

  1. class StrVec {
  2. public:
  3. StrVec &operator=(std::initializer_list<std::string>);
  4. };
  5. StrVec &StrVec::operator=(initializer_list<string> il)
  6. {
  7. // alloc_n_copy 分配内存空间并从给定范围内拷贝元素
  8. auto data = alloc_n_copy(il.begin(), il.end());
  9. free();
  10. elements = data.first;
  11. first_tree = cap = data.second;
  12. return *this;
  13. }

递增和递减运算符

为了与内置版本保持一致,后置运算符应该返回对象的原值,返回的形式是一个值而非引用

  1. class StrBlobPtr {
  2. public:
  3. StrBlobPtr& operator++(); // 前置运算符
  4. StrBlobPtr operator++(int); // 后置运算符
  5. };
  6. StrBlobPtr& StrBlobPtr::operator++()
  7. {
  8. // 如果指针已经指向了容器的尾后位置,则无法递增它
  9. check(curr, "increment past end of StrBlobPtr");
  10. ++curr;
  11. return *this;
  12. }
  13. StrBlobPtr StrBlobPtr::operator++(int)
  14. {
  15. // 此处无须检查有效性,调用前置递增运算时才需要检查
  16. StrBlobPtr ret = *this;
  17. ++*this;
  18. return ret;
  19. }

成员访问运算符

  1. class StrBlobPtr {
  2. public:
  3. std::string& operator*() const {
  4. auto p = check(curr, "dereference past end");
  5. return (*p)[curr]; // (*p) 是对象所指的 vector
  6. }
  7. std::string* operator->() const {
  8. // 将实际工作委托给解引用运算符
  9. return & this->operator*();
  10. }
  11. };

我们能令 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;
    }
};