# 运算符重载

> [!info] 学习导航
> 返回：[[C++ 学习指南]]
> 前置知识：[[计算机系/C++/06 类与对象基础|类与对象基础]]

T1.oprator+(t2)

```cpp
Oprator+(t1,t2)
```


T1+t2

做出对象：调用对象

右侧对象L作为参数北川金

**C++ 重载运算符和重载函数**

C++ 允许在同一作用域中的某个**函数**和**运算符**指定多个定义，分别称为**函数重载**和**运算符重载**。

重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明，但是它们的参数列表和定义（实现）不相同。

当您调用一个**重载函数**或**重载运算符**时，编译器通过把您所使用的参数类型与定义中的参数类型进行比较，决定选用最合适的定义。选择最合适的重载函数或重载运算符的过程，称为**重载决策**。

**C++ 中的函数重载**

在同一个作用域内，可以声明几个功能类似的同名函数，但是这些同名函数的形式参数（指参数的个数、类型或者顺序）必须不同。您不能仅通过返回类型的不同来重载函数。

下面的实例中，同名函数 **print()** 被用于输出不同的数据类型

函数重载与函数的返回值无关

小数是double类型

**C++ 中的运算符重载**

您可以重定义或重载大部分 C++ 内置的运算符。这样，您就能使用自定义类型的运算符。

重载的运算符是带有特殊名称的函数，函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样，重载运算符有一个返回类型和一个参数列表。

```cpp
Box operator+(const Box&);
```

声明加法运算符用于把两个 Box 对象相加，返回最终的 Box 对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数，那么我们需要为每次操作传递两个参数，如下所示：

```cpp
Box operator+(const Box&, const Box&);
```

下面的实例使用成员函数演示了运算符重载的概念。在这里，对象作为参数进行传递，对象的属性使用 **this** 运算符进行访问，如下所示：

```cpp
   Box operator+(const Box& b)
   {
     Box box;
     box.length = this->length + b.length;
     box.breadth = this->breadth + b.breadth;
     box.height = this->height + b.height;
     return box;
```

   }实际调用时

Box1+Box2等价于Box1.operator+(Box2)

在上述教程中，复合赋值运算符重载没有被提到，但这往往也是经常需要用到的，而且对于新人来讲比较容易出错，我来补充一下吧。

**复合赋值操作符是指 += ， \*= ， -= 这一类由基本算数运算符（ + 、 - 、 \* 、 / )或位运算符(| 、 & 、~等）加 = 号构成的运算符**。它们把左右操作数进行相应运算后的结果赋值给左操作符。例如：a += b； 意味着 a = a + b；  复合赋值操作符的返回值，默认是左值。比如a += b；中的a。

在进行复合赋值操作符重载定义的时候，需要注意，**其返回值，应该为（ \* this）<类成员>或者第一个也就是左操作数<非成员函数>**。例如：（假设已经自定义算数或位操作运算符）

```cpp
className & className::operator +=(className & right)
{
  return (*this) = (*this) + right;
}
className& operator +=(className& left, className& right)
{
  return left = left + right;
}
```

**className A = a，B = b;**（A，B是对象名，a，b是值）。

**A += B;** （此时A = a + b，整个表达式返回值是a + b）。

重写复合赋值操作符的时候，一定要记得，要把运算结果赋给左值，再返回左值。如果直接返回结果，我们将不会被调用处看到左值被运算的效果。


```cpp
// 重载 + 运算符，用于把两个 Box 对象相加
  Box operator+(const Box& b)
  {
   Box box;
   box.length = this->length + b.length;
   box.breadth = this->breadth + b.breadth;
   box.height = this->height + b.height;
   return box;
  }
```

**可重载运算符/不可重载运算符**

下面是可重载的运算符列表：

| 双目算术运算符 | +  (加)，-(减)，\*(乘)，/(除)，% (取模)                       |
| -------------- | ------------------------------------------------------------ |
| 关系运算符     | ==(等于)，!= (不等于)，< (小于)，> (大于)，<=(小于等于)，>=(大于等于) |
| 逻辑运算符     | \|\|(逻辑或)，&&(逻辑与)，!(逻辑非)                          |
| 单目运算符     | +  (正)，-(负)，\*(指针)，&(取地址)                           |
| 自增自减运算符 | ++(自增)，--(自减)                                           |
| 位运算符       | \|  (按位或)，& (按位与)，~(按位取反)，^(按位异或),，<< (左移)，>>(右移) |
| 赋值运算符     | =,  +=, -=, \*=, /= , % = , &=, \|=, ^=, <<=, >>=             |
| 空间申请与释放 | new,  delete, new[ ] , delete[]                              |
| 其他运算符     | **()**(函数调用)，**->**(成员访问)，**,**(逗号)，**[]**(下标) |

下面是不可重载的运算符列表：

- **.**：成员访问运算符
- **.\***, **->\***：成员指针访问运算符
- **::**：域运算符
- **sizeof**：长度运算符
- **?:**：条件运算符
- **#**： 预处理符号

## 一元运算符重载

一、递增运算符和递减运算符

1、递增和递减一般是改变对象的状态，所以一般是重载为成员函数。

2、重载递增递减，一定要和指针的递增递减区分开。因为这里的重载操作的是对象，而不是指针(由于指针是内置类型，指针的递增递减是无法重载的)，所以一般情况的递增递减是操作对象内部的成员变量。

3、递增和递减分为前置和后置情况，a = ++b;(前置)， a = b++;(后置)。因为符号一样，所以给后置版本加一个int形参作为区分，这个形参是0，但是在函数体中是用不到的，只是为了区分前置后置。

```cpp
// 例如这里的重载递增就是为了增加pos的值
class ca
{
public:
  int pos;
// 前置递增就是增加当前对象的pos的值，并且返回当前对象
  ca operator++(){
    pos++;
    return *this;
  }
```

或者

```cpp
Ca operator ++()
{
Ca temp;
Temp.pos=++pos;
Return temp;
 }
// 后置递增就是增加当前对象的pos的值，并且返回增加pos之前的该对象
  ca operator++(int){
    ca ret = *this;
    ++*this; // 这个会调用上面的函数，其实这里可以换成pos++;
    return ret;
  }
```

或者

```cpp
Ca operator ++(int )
{
Ca temp;
temp.pos=pos++;
Return temp;
}
};
```

在前缀递增时，若想要实现 **++(++a)** 这种连续自加，就要返回其对象的引用，这样才能保证操作的是同一块内存空间，否则就只是单纯的赋值操作，原来的对象并未被修改。

```cpp
Complex& Complex::operator ++() {
  ++i;
  ++j;
  return *this;
}
```

二、一元减运算符

```cpp
// 重载负运算符（ - ）
   Distance operator- ()
   {
     feet = -feet;
     inches = -inches;
     return Distance(feet, inches);
   }
```

三、逻辑非运算符

## 二元运算符重载

```cpp
// 重载 + 运算符，用于把两个 Box 对象相加
  Box operator+(const Box& b)
  {
   Box box;
   box.length = this->length + b.length;
   box.breadth = this->breadth + b.breadth;
   box.height = this->height + b.height;
   return box;
  }
```

当 2 个对象相加时是没有顺序要求的，但要重载 **+** 让其与一个数字相加则有顺序要求，可以通过加一个友元函数使另一个顺序的输入合法。

Public:

```cpp
      A operator+(const A & obj);
      A operator+(const int b);
  friend A operator+(const int b, A obj);
A A::operator +(const A& obj) // 重载+号用于 对象相加
{
  return this->a+obj.a;
}
A A::operator+(const int b) // 重载+号用于 对象与数相加
{
  return A(a+b);
}
A operator+(const int b, A obj)
{
  return obj+b; // 友元函数调用第二个重载+的成员函数 相当于 obj.operator+(b);
}
```

关系运算符重载

```cpp
   bool operator <(const Distance& d)
   {
     if(feet < d.feet)
     {
      return true;
     }
     if(feet == d.feet && inches < d.inches)
     {
      return true;
     }
     return false;
   }
```

输入输出运算符重载

C++ 能够使用流提取运算符 >> 和流插入运算符 << 来输入和输出内置的数据类型。您可以重载流提取运算符和流插入运算符来操作对象等用户自定义的数据类型。

在这里，有一点很重要，我们需要把运算符重载函数声明为类的友元函数，这样我们就能不用创建对象而直接调用函数。

   friend ostream &operator<<( ostream &output,

​                    const Distance &D )

```cpp
   {
     output << "F : " << D.feet << " I : " << D.inches;
     return output;
   }
friend istream &operator>>( istream &input, Distance &D )
   {
     input >> D.feet >> D.inches;
     return input;
   }
```

因为最后返回的还是 os，所以可以循环使用，也就是写成:

Cout <<d1<<d2

赋值运算符

用于创建一个对象，比如拷贝构造函数

```cpp
   void operator=(const Distance &D )
   {
     feet = D.feet;
     inches = D.inches;
   }
```

- **尽量让运算符重载的参数是类类型**（如 **operator+(const Number&)**），而不是基本类型（如 **operator+(double)**）。
- **用 explicit 防止隐式转换**，避免意外的二义性。

## 相关内容
- [[计算机系/C++/16 STL 算法、函数对象与 Lambda|STL 算法、函数对象与 Lambda]]

---

## 学习导航
- 上一篇：[[计算机系/C++/08 类的高级成员与静态成员|类的高级成员与静态成员]]
- 返回目录：[[C++ 学习指南]]
- 下一篇：[[计算机系/C++/10 继承与赋值兼容|继承与赋值兼容]]
