C++ 面向对象-运算符的重载

本文最后更新于:2022年4月22日 上午

面向对象-运算符的重载

加号(+)运算符的重载

C++程序中,两个数字进行相加,编译器是能够正确的计算结果的,但是如果是两个对象进行相加,编译器将不知道该如何处理这个情况,这个时候,我们给了重载这个对象的 加号运算符,让编译器明确当这两个对象进行相加的时候该如何处理

class User {

public:
  int age;

  User(int age) {
    this->age = age;
  }

  // 重写 + 运算符
  User& operator+(User& p) {
    User newp(this->age + p.age);

    return newp;
  }
};

void two_test1() {

  User p1(10);
  User p2(20);
  
  // 重写 加号运算符之后,编译器就知道两个 User 对象进行相加的时候需要如何操作
  User p3 = p1 + p2;

  // 默认是报错的
  cout << p3.age << endl;

}

输出

30

左移(<<)运算符的重载

之前我们使用 cout << xxx; 只能打印一些基本值如 stringint等,如果我们一个 class 中有很多成员属性
我们希望能够将这些成员属性都打印出来(类似于 java 中的重写 toString 方法),我们就可以重写 左移运算符

因为我们一直是使用 cout 进行打印的,cout 是在 输出流(ostream) 中的,所以我们这里接受的第一个参数是 ostream& cout

void operator<<(ostream& cout, User& u) {
  cout << "user age:" << u.age << endl;
};

测试 (不能连续传入的参数,如: cout << u << endl; 会报错 )

cout << u ;

输出

user age:10

优化:void operator<<(ostream &out, User &u) 的方式是无返回值的方式,意味着 cout << u << xxx;

在接收到 第一个合法参数后就不允许再次接收了其它的参数了 ,如果要进行优化的话,我们可以返回一个 cout,这样就就能继续接收了

如果需要在重载的左移运算符中访问 类的private属性,需要在 class中添加全局函数友元

优化后

ostream& operator<<(ostream &out, User &u) {
  out << "user age:" << u.age << endl;
  return out;
};

测试

cout << u << endl;

输出

user age:10

自增(++)运算符的重载

对于数字的 ++,编译器知道该如何处理,但是对于对象的++,编译器就不知道该如何处理了,这时候就需要重载该对象的自增运算符了

因为自增分为两种,前置自增和后置自增 (++i)和(i++);

class MyInt {
  // 因为访问了私有成员,需要添加友元
  friend ostream& operator<<(ostream& cout, MyInt n);

public:

  MyInt() {
    count = 0;
  }

  // 重载 前置++
  MyInt& operator++() {
    count++;
    return *this;
  }

  // 重载 后置++ 通过占位参数来区分后置++
  MyInt operator++(int) {
    MyInt temp = *this;

    count++;

    return temp;
  }

private:
  int count;
  
};

// 这里重写 左移运算符的时候需要注意第二个参数不能写成引用,因为 后置++ 传入的不是引用,而是值
ostream& operator<<(ostream& cout, MyInt n) {
  
  cout << n.count;
  return cout;
}

void two_test3() {
  MyInt n1;

  // 前置++
  cout << ++(++n1) << endl;  // 2 
  cout << n1 << endl;        // 2

  MyInt n2;
  cout << n2++ << endl;      // 0
  cout << n2 << endl;        // 1

}


// ...
two_test3();

输出

2
2
0
1

赋值(=)运算符的重载

当我们创建一个 class的时候,编译器默认会帮我提供一个赋值运算符,但是它内部实现的是浅拷贝,不是深拷贝


class Person4 {

public:
  int* age;
  Person4(int age){
    this->age = new int(age);
  }

  // 重载 赋值运算符 
  Person4& operator=(Person4& p) {

    if (age != NULL) {
      delete age;
      age = NULL;
    }

    age = new int(*p.age);
    return *this;
  }

  ~Person4() {
    if (age != NULL) {
      delete age;
      age = NULL;
    }
  }

};

void two_test4() {
  Person4 p1(10);
  Person4 p2(20);
  p1 = p2;

  cout << *p1.age << endl;
}

输出

20

如果在上面的 class 中没有重载赋值运算符的话,编译运行的时候就会报以下的错误

Project.exe 已触发了一个断点。

关系(==、>、<)运算符的重载

重载关系运算符可以让我们 自定义 在两个对象进行比较时操作

class Person5 {

public:
  string name;
  int age;

  Person5(string name, int age) :name(name), age(age) {};

  // 重载关系运算符 ==, > < 运算符也是相似的
  bool operator==(Person5& p) {
    if (this->name == p.name && this->age == p.age) {
      return true;
    }
    return false;
  }
};

void two_test5() {
  Person5 p1("小明", 18);
  Person5 p2("小明", 18);


  cout << (p1 == p2) << endl;
}

// ...
two_test5();

输出

1

函数调用运算符的重载

将类实例化出来的对象 通过函数的方式调用,叫做仿函数,仿函数没有固定写法,非常灵活

class AddNumber {
public:
  int operator()(int num1, int num2) {
    return num1 + num2;
  }
};

void two_test6() {

  AddNumber addnumber;
  int num = addnumber(10, 20);

  cout << num << endl;
}

// ...

two_test6();

输出

30

总结

C++中可重载这些运算符让我们在某些情况下的编程变得更加便利,不会出现太多的不确定性。在js中,js引擎会根据不同的类型做不同的处理,且无法重载,以至于在前端面试题中经常出现这些js特性衍生出来的笔试题

two_test6


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议,转载请注明出处。