C++之重载运算符

127 阅读5分钟

前言

在C++语言中,官方实现的运算符可以满足我们的很多的需求,但是,为了实现我们各种各样的功能,我们不可避免的需要自己实现运算符的功能,所以,我们需要学会怎么实现运算符的重载。

C++中可以重载的运算符有

  1. 算术运算符:+(加法)、-(减法)、*(乘法)、/(除法)、%(取模)
  2. 自增和自减运算符:++(自增)、--(自减)
  3. 关系运算符:==(相等)、!=(不相等)、<(小于)、>(大于)、<=(小于等于)、>=(大于等于)
  4. 位运算符:&(按位与)、|(按位或)、^(按位异或)、~(按位取反)、<<(左移)、>> (右移)
  5. 逻辑运算符:&&(逻辑与)、||(逻辑或)、!(逻辑非)
  6. 赋值运算符:=(赋值)、+=(加赋值)、-=(减赋值)、*=(乘赋值)、/=(除赋值)、%=(取模赋值)、&=(按位与赋值)、|=(按位或赋值)、^=(按位异或赋值)、<<=(左移赋值)、>>=(右移赋值)
  7. 下标运算符:
  8. 函数调用运算符:()(函数调用)
  9. 成员访问运算符:->(指针成员访问)
  10. 类型转换运算符:type()(类型转换)
  11. 其他运算符:new(动态分配内存)、new[](动态分配数组)、delete(释放内存)、delete[](释放数组内存)

注意

某些运算符(例如 ..*)无法被重载。 重载运算符时,必须遵循特定的规则和约定。

实现加法运算符(减法-,乘法*,除法/和取模%符号都一样)

#include <iostream>

class Point{
public:
 Point(int x = 0int y = 0) : x(x), y(y) {}
 Point operator+(const Point& point){
  return  Point(x + point.x, y + point.y);
 }
 void PrintValue(){
  std::cout << "x = " << x << ", y = " << y << std::endl; 
 }
 
 void PrintOther(const Point& point){
  std::cout << "x = " << point.x << ", y = " << point.y << std::endl;
 }
private:
 int x;
 int y;
};

int main(){
 Point point_1(34);
 Point point_2(554);
 Point point_3(point_1 + point_2);
 point_3.PrintValue();
 
 point_3.PrintOther(point_1);
 
 return 0; 
}

其实,当操作符的两边都有变量的话,都可以使用这种方式实现,也就是说,除了算术运算符之外,还有==(相等)、!=(不相等)、<(小于)、>(大于)、<=(小于等于)、>=(大于等于)、&(按位与)、|(按位或)、^(按位异或)、<<(左移)、>> (右移)、&&(逻辑与)、||(逻辑或)、(赋值)、+=(加赋值)、-=(减赋值)、*=(乘赋值)、/=(除赋值)、%=(取模赋值)、&=(按位与赋值)、|=(按位或赋值)、^=(按位异或赋值)、<<=(左移赋值)、>>=(右移赋值)都可以使用

我们知道,自增符号并不是操作符的两边都是变量,它只是一边有变量,代码可以这样实现


#include <iostream>

class Point{
public:
 Point(int x = 0int y = 0) : x(x), y(y) {}
 //这是实现前缀加加
 Point& operator++(){
  x += 1;
  y += 1;
  return  *this;
 }
 //后缀加加需要加一个参数,只能是int
 Point& operator++(int){  //这个是为了区分前缀和后缀自增
  x += 1;
  y += 1;
  return  *this;
 }
 void PrintValue(){
  std::cout << "x = " << x << ", y = " << y << std::endl; 
 }
private:
 int x;
 int y;
};

int main(){
 Point point_1(34);
 //++point_1;
 point_1++;
 point_1.PrintValue();

 return 0; 
}

除了++(自增)之外,还有--(自减)、~(按位取反)、!(逻辑非)都可以使用

除了上面两种情况,那么下标运算符怎么使用呢?

#include <iostream>
#include <vector>

class CVector{
public:
 CVector(){
  vec = {1234};
 }
 int operator[](int index){
  return vec[index];
 }
private:
 std::vector<int> vec;
};

int main(){
 CVector vec;
 std::cout << vec[0] << std::endl;
 return 0; 
}

这个代码只是让了解下标运算符怎么使用,代码没有做防御性,等你们自己做项目时,记得防御性编程

下面实现函数调用运算符(),代码如下

#include <iostream>

class Adder{
public:
 int operator()(int x, int y){
  return  x + y;
 }
};

int main(){
 Adder adder;
 std::cout << adder(34) << std::endl;
 return 0; 
}

下面实现成员访问运算符->

#include <iostream>

class Adder{
public:
 Adder* operator->(){
  std::cout << "调用成员访问运算符->..." << std::endl;
  return this;
 }
 
 void TestFunc(){
  std::cout << "TestFunc()..." << std::endl; 
 }
};

int main(){
 Adder* adder = new Adder();
 adder->TestFunc();
 return 0;
}

下面实现类型转换运算符,这里只实现了int和double,其他类型一样,读者可自行实现

#include <iostream>
using namespace std;

class MyClass{
public:
 MyClass(int x = 0double y = 0.0) : value(x), doubleValue(y){}
 
 operator int() const{
  return value; 
 }
 
 operator double() const{
  return doubleValue; 
 }
private:
 int value;
 double doubleValue;
};

int main()
{
 MyClass myclass(9993.14);
 int value = myclass;
 double doubleValue = myclass;
 cout << "value = " << value << ", doubleValue = " << doubleValue << endl;
   return 0;
}

下面实现动态分配运算符(这个代码是笔者直接复制chatgpt的,读者可以自行实现看看)

#include <iostream>
#include <cstdlib> // 包含 malloc 和 free

class MyClass {
public:
    // 重载 new 运算符
    void* operator new(size_t size) {
        std::cout << "自定义 new: 分配 " << size << " 字节内存" << std::endl;
        void* p = std::malloc(size); // 使用 malloc 分配内存
        if (!p) {
            throw std::bad_alloc(); // 分配失败时抛出异常
        }
        return p;
    }

    // 重载 delete 运算符
    void operator delete(void* p) {
        std::cout << "自定义 delete: 释放内存" << std::endl;
        std::free(p); // 使用 free 释放内存
    }

    // 构造函数
    MyClass() {
        std::cout << "构造 MyClass" << std::endl;
    }

    // 析构函数
    ~MyClass() {
        std::cout << "析构 MyClass" << std::endl;
    }
};

int main() {
    MyClass* obj = new MyClass(); // 触发自定义 new 运算符

    delete obj; // 触发自定义 delete 运算符

    return 0;
}

笔者再实现一下赋值运算符

#include <iostream>

class Point{
public:
 Point(int x = 0int y = 0) : x(x), y(y) {}
 Point& operator=(const Point& point){
  if(&point != this){  //防止自我赋值
   x = point.x;
   y = point.y; 
  }
  return *this;
 }
 void PrintValue(){
  std::cout << "x = " << x << ", y = " << y << std::endl; 
 }
private:
 int x;
 int y;
};

int main(){
 Point point_1(34);
 Point point_3;
 point_3 = point_1;
 point_3.PrintValue();
 
 return 0; 
}

除了上面的运算符,还有重载输出/输入流运算符

#include <iostream>

class Point {
public:
 Point(int x = 0int y = 0) : x(x), y(y) {}
 friend std::ostream& operator<<(std::ostream& os, const Point& point) {
  os << "x = " << point.x << ",y = " << point.y << std::endl;
  return os;
 }

 friend std::istream& operator>>(std::istream& is, Point& point) {
  is >> point.x >> point.y;
  return is;
 }
private:
 int x;
 int y;
};

int main() {
 Point point_1(34);
 std::cout << point_1;

 Point point_2;
 std::cin >> point_2;
 std::cout << point_2;

 return 0;
}