0%

99. C++运算符重载

  • 重载运算符规则

  1. 重载意义

    函数重载(Function Overloading)可以让一个函数名有多种功能,在不同情况下进行不同的操作;

    运算符重载(Operator Overloading)也是一个道理,同一个运算符可以有不同的功能;

    C++中预定义的运算符的操作对象只能是基本数据类型。但实际上,对于许多用户自定义类型(例如类),也需要类似的运算操作。这时就必须在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型执行特定的操作。运算符重载的实质是函数重载,它提供了C++的可扩展性,也是C++最吸引人的特性之一。

    比如,我们定义两个string类对象a,b后我们之所以可以使用+运算,是因为string类重载了+运算符。

  2. 语法

    1
    2
    3
    4
    <返回类型说明符> operator <运算符符号>(<参数表>)
    {
    <函数体>
    }
  3. 哪些运算符可以被重载,哪些运算符不能被重载

    • 可以被重载的运算符:

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

      位操作运算符:& , | , ~ , ^ , << , >>

      逻辑运算符:! , && , ||

      比较运算符:< , > , >= , <= , == , !=

      赋值运算符:= , += , -= , *= , /= , %= , &= , |= , ^= , <<= , >>=

      其他运算符:[] , () , -> , ,(逗号运算符) , new , delete , new[] , delete[] , ->*

    • 不允许的运算符重载:

      . , .* , :: , ?: ,siezof

  • 重载运算符函数在类内部

  1. Notes:

    1. 运算符重载函数是类的成员函数时>

      1. 注意

        要是内部的,必须得是<=1个参数,不需要加 friend(友元函数)

      2. 语法(在类内声明,在类外实现时的代码)
        1
        2
        3
        4
        5
        <返回类型说明符> <类名>::operator <运算符符号>(<参数表>)
        {
        <函数体>
        }

    2. 运算符重载函数不是类的成员函数时(或者说当运算符函数是非成员函数时

      1. 函数在类中声明的时候需要在前面加上 friend(友元函数),不管参数是不是类的对象
      2. 函数的参数与该运算符作用的运算对象数量一样多
    3. 为什么用 const&:

      1. const

        1. 我们不希望在这个函数中对用来进行赋值的“原版”做任何修改。函数加上const后缀的作用是表明函数本身不会修改类成员变量。
        2. 加上const,对于const的和非const的实参,函数就能接受;如果不加,就只能接受非const的实参。
      2. &(引用):

        这样可以避免在函数调用时对实参的一次拷贝,提高了效率。

  1. 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    /**
    * Author: Dgimo
    * Date: 2020/3/30
    * TODO: 类运算符重载
    * Describe:
    */


    #include <iostream>

    using namespace std;


    typedef struct {
    int first;
    int second;
    } TestStuct;


    /*
    * 类运算符重载
    */
    class TestClass {
    private:
    int first;
    int second;

    public:

    TestClass(int, int);
    TestClass();

    void show(string);

    TestClass operator+(const TestClass &);
    TestClass operator+(const TestStuct &);
    friend TestClass operator+(const TestClass &, const TestClass &); // 如果不是内部的,得加friend 声明不是内部的,如果不是两个会怎样?见下面!!!
    friend TestClass operator+(const TestStuct &, const TestStuct &); // 只要在类内部定义的运算符重载,就需要加上friend

    // TestClass operator+(const TestClass &, const TestClass &); // 要是内部的,必须得是<=1个参数,不需要加 friend


    };

    TestClass::TestClass(int first, int second) {
    this->first = first;
    this->second = second;
    }

    TestClass::TestClass()
    {
    this->first = 1;
    this->second = 2;
    }

    TestClass TestClass::operator+(const TestClass &tc) { // TestClass + TestClass
    return TestClass(first + tc.first, second + tc.second);
    }

    TestClass TestClass::operator+(const TestStuct & ts) { // TestClass + TestStruct
    return TestClass(first + ts.first, second + ts.second);
    }

    TestClass operator+(const TestClass &fir, const TestClass &sec) { // 全局:TestClass + TestCLass 实际上和 TestClass TestClass::operator+(const TestClass &tc)
    return TestClass(fir.first + sec.first, fir.second + sec.second);
    }

    TestClass operator+(const TestStuct &fir, const TestStuct &sec) {
    return TestClass(fir.first + sec.first, fir.second + sec.second);
    }

    void TestClass::show(string str) {
    cout << str << ": " << "first: " << this->first << ", second: " << this->second << endl;
    }

    int main()
    {
    TestClass tc1, add_tc, add_tc1, add_ts;
    TestClass tc2 = TestClass(2, 3);

    TestStuct ts1, ts2;
    ts1.first = 1, ts1.second = 2;
    ts2.first = 10, ts2.second = 20;


    tc1.show("tc1");
    tc2.show("tc2");

    add_tc = tc1 + tc2; // TestClass + TestClass
    add_tc.show("add_tc");

    add_tc1 = add_tc + ts1; // TestClass + TestStruct
    add_tc1.show("add_tc1");

    add_ts = ts1 + ts2; // TestStrcut + TestStrcut
    add_ts.show("add_ts");

    return 0;
    }
  • 重载运算符在函数外部

  1. 暂时未发现 Bug

  2. 代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    /**
    * Author: Dgimo
    * Date: 2020/3/31
    * TODO: 结构体运算符重载
    * Describe:
    */


    #include <iostream>

    using namespace std;

    /*
    * 结构体运算符重载
    */
    typedef struct {
    int first;
    int second;
    } TestStuct;

    TestStuct operator+(TestStuct ts1, TestStuct ts2)
    {
    TestStuct res;
    res.first = ts1.first + ts2.first;
    res.second = ts1.second + ts2.second;
    return res;
    }

    bool show(TestStuct ts)
    {
    cout << "first: " << ts.first << ", second: " << ts.second << endl;
    }

    int main()
    {
    TestStuct ts1;
    ts1.first = 1;
    ts1.second = 2;

    TestStuct ts2;
    ts2.first = 2;
    ts2.second = 3;

    TestStuct res_t = ts1 + ts2;
    show(res_t);
    return 0;
    }
  • <<>> 只能定义友元函数

  1. 原因

    定义为成员函数,那么就隐含this指针了对吧,你要知道重载其实也是一种函数,那么函数就有调用他的对象,如果是成员函数,那么调用他的对象就肯定是想对应的类对象了对吧,但是<<和>>调用的对象肯定只能是cout或者cin对吧,那么久不能定义为成员函数了,只有定义成友元,那么就可以把cin,cout作为一个参数传进你重载的操作符函数里面去了

  2. 语法:

    1. 在类中:

      1
      friend ostream &operator <<(ostream &out, const ElemType &e);
    2. 在外面:

      1
      ostream &oprator <<(ostream &out, const ElemType &e);
  3. 代码

    1. 头文件(.h)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      #ifndef ELEMTYPE_H
      #define ELEMTYPE_H

      #include <iostream>

      using namespace std;

      class ElemType
      {
      public:
      int data;
      ElemType();
      ElemType(int);

      friend ostream &operator <<(ostream &out, const ElemType &e); // Success

      protected:

      private:
      };

      #endif // ELEMTYPE_H
    2. 源文件(.cpp)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      #include "ElemType.h"

      ElemType::ElemType()
      {
      //ctor
      this->data = 0;
      }

      ElemType::ElemType(int data)
      {
      this->data = data;
      }


      ostream& operator <<(ostream &out, const ElemType &e)
      {
      out << e.data;
      return out;
      }