C++ 重载运算符 (1)

168 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情

在 C++ 中,重载运算符是指为类定义的运算符函数,这些运算符函数允许在使用类对象作为运算符的操作数时使用标准的运算符符号,而不必使用函数调用的方式。

本文将介绍C++的各种用途重载运算符中的算术运算符和关系运算符


在 C++ 中,可以重载的运算符包括:

  • 算术运算符:+-*/%++--
  • 关系运算符:==!=<><=>=
  • 逻辑运算符:&&||!
  • 位运算符:&|^~>><<
  • 赋值运算符:=+=-=*=/=%=&=|=^=<<=>>=
  • 下标运算符:[]
  • 取地址运算符:&
  • 解引用运算符:*
  • 成员访问运算符:.*->*
  • 函数调用运算符:()
  • 逗号运算符: ,
  • 内存分配运算符:newdelete 不能重载的运算符,包括:
  • 三目运算符(?:)不能重载。
  • 作用域解析运算符(::)不能重载。
  • 关键字 sizeof 不能重载。
  • 类成员访问运算符(.)不能重载
  • 类成员指针访问运算符(.*)不能重载

此外,在 C++ 中,还可以创建新的运算符,通过使用关键字 operator 和其他符号来定义。

算术运算符:+-*/%++--

重载 operator+

class Complex {
 public:
  Complex(double real = 0.0, double imag = 0.0)
      : real_(real), imag_(imag) {}

  Complex operator+(const Complex& other) const {
    return Complex(real_ + other.real_, imag_ + other.imag_);
  }

 private:
  double real_;
  double imag_;
};
Complex c1(1, 2);
Complex c2(3, 4);
Complex c3 = c1 + c2; 

重载 operator-

举个例子,比如我想要在std::list后面添加元素,又想要形式化的添加

想要达到这种效果:

int main() {
  std::list<int> x;
  x - 1 - 2 - 3 - 4;
  for (auto v : x) {
    std::cout << v << " \n";
  }
  return 0;
}

放在其他语言中可能就不好办,但是我们可以!

只需要来一个重载即可(侵入式的修改了list的operator-)

template <typename T> std::list<T> &operator-(std::list<T> &lis, T value) {
  lis.push_back(value);
  return lis;
}

重载 operator *

重载* 和 +-类似,但是要注意,这两个的区别

Complex operator*() {}
Complex operator*(const Complex& other) {}

乘是二元操作对象,解引用是一元操作对象。

class Complex {
 public:
  Complex(double real = 0.0, double imag = 0.0)
      : real_(real), imag_(imag) {}

  Complex operator*(const Complex& other) const {
    return Complex(real_ * other.real_ - imag_ * other.imag_,
                   real_ * other.imag_ + imag_ * other.real_);
  }

 private:
  double real_;
  double imag_;
};

重载 operator /

将 上面 operator-改为operator/ 来试试呢?

template <typename T> std::list<T> &operator/(std::list<T> &lis, T value) {
  lis.push_back(value);
  return lis;
}

重载 / 可以用在什么地方呢?

一个可用的地方:从A集合中去除B集合的元素,可以写为 A / B

就可以采用重载来干这一件事情。

重载 operator %

和 + - * / 类似,二元操作运算符。

可以用到哪些地方呢,在不同的语言中,对负数执行取模运算,结果有可能会是不同的。

比如,我们定义的模空间其实是 [0, m),但是语言自带的取模却可能是负数

这个时候,重载取模让我们自己来掌控细节就变得重要了。

重载 ++ 和 -- 运算符

重载 ++ 和 -- 两者类似。

分为 x++ 和 ++x 这两种形式。

为了区别他们,在重载的时候 x++这种形式会多一个参数来重载区分。但是这个参数是无用的。

x++在语义上,一般表示先给表达式的值,然后进行++,而++x是先++,在返回本身。

可以看出,x++可能会存在,拷贝,但是,有了自定义运算符,我们也可以反常识,两者都直接先++再返回

struct E {
  int x;
  E &operator++() {
    x++;
    return *this;
  }
  E &operator++(int) {
    x++;
    return *this;
  }
};

-- 和 ++ 类似,不再列举。

关系运算符:==!=<><=>=

现在,用一个string的例子重载关系运算符

string 类如下:

class string {
  char *buf;
  int len;
  int cap;
  void resize(size_t siz) {
    char *new_buf = new char[siz];
    strcpy(new_buf, buf);
    delete buf;
    buf = new_buf;
    cap = siz;
  }

public:
  string() : buf(new char[1024]), len(0), cap(1024) {}
  string(const char *str) : buf(new char[1024]), len(0), cap(1024) {
    int x{};
    if ((x = strlen(str)) > cap) {
      resize(x * 2 + 1);
    }
    strcpy(buf, str);
  }
};

所有比较操作就直接调用的 库函数中的strcmp。

重载 operator ==

== 是 一个二元操作符,需要传入一共参数。

bool operator==(const string &rhs) { return strcmp(buf, rhs.buf) == 0; }

重载 operator !=

和 == 相反,所以可以直接调用 operator ==

bool operator!=(const string &rhs) { return !(*this == rhs); }

重载 operator >

> 和 <= 是一对相反的运算符

二元运算符

bool operator>(const string &rhs) { return strcmp(buf, rhs.buf) > 0; }

重载 operator <=

bool operator<=(const string &rhs) { return strcmp(buf, rhs.buf) <= 0; }

重载 operator <

< 和 >= 是一对相反的运算符

bool operator<(const string &rhs) { return strcmp(buf, rhs.buf) < 0; }

重载 operator >=

bool operator>=(const string &rhs) { return strcmp(buf, rhs.buf) >= 0; }