C++ 重载运算符

时间:2021-01-28 17:38:27

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

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

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

声明加法运算符用于把两个 Box 对象相加,返回最终的 Box 对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。


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

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

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

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

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



1 一元运算符重载

一元运算符只对一个操作数进行操作:

  • 递增运算符( ++ )和递减运算符( -- )
  • 一元减运算符,即负号( - )
  • 逻辑非运算符( ! )

一元运算符通常出现在它们所操作的对象的左边,比如 !obj、-obj 和 ++obj,但有时它们也可以作为后缀,比如 obj++ 或 obj--。

下面的实例演示了如何重载一元减运算符( - )。

#include <iostream>

using namespace std;

class Time
{
public:
    //自定义构造函数
    Time(int t)
    {
        second = t;
    }

    //获得distance成员数据值
    int GetDistance()
    {
        return second;
    }

    //重载前缀递增运算符( ++ )
    //前置递增就是增加当前对象的值,并且返回当前对象
    Time operator ++()
    {
        return Time(second++);
        //distance++;
        //return *this; //也可以使用this指针
    }

    //重载后缀递增运算符( ++ )
    //后置递增就是增加当前对象的值,并且返回增加值之前的该对象
    Time operator ++(int)
    {
        Time origin = *this; //保存原先未改变的对象
        second++;
        return origin;
    }

    //重载前缀递减运算符( -- )
    //前置递减就是减少当前对象的值,并且返回当前对象
    Time operator --()
    {
        return Time(second--);
        //distance--;
        //return *this; //也可以使用this指针
    }

    //重载后缀递减运算符( -- )
    //后置递减就是减少当前对象的值,并且返回减少值之前的该对象
    Time operator --(int)
    {
        Time origin = *this; //保存原先未改变的对象
        second--;
        return origin;
    }

    //重载一元减运算符,即负号(-)
    Time operator -()
    {
        return Time(-second);
    }

private:
    int second;
};

int main() 
{
    Time d1(5);
    ++d1; //前缀递增
    d1++; //后缀递增
    cout << "重载运算符(++):" << d1.GetDistance() << endl;
    --d1; //前缀递减
    --d1; //后缀递减
    cout << "重载运算符(--):" << d1.GetDistance() << endl;
    d1 = -d1;
    cout << "重载运算符(-):" << d1.GetDistance() << endl;

    return 0;
}

/*
输出结果:

重载运算符(++):7
重载运算符(--):5
重载运算符(-):-5
*/

注意:

  • 重载递增递减,一定要和指针的递增递减区分开。因为这里的重载操作的是对象,而不是指针(由于指针是内置类型,指针的递增递减是无法重载的),所以一般情况的递增递减是操作对象内部的成员变量。
  • 递增和递减分为前置和后置情况,a = ++b;(前置), a = b++;(后置)。因为符号一样,所以给后置版本加一个int形参作为区分,这个形参是0,但是在函数体中是用不到的,只是为了区分前置后置。



2 二元运算符重载

二元运算符需要两个参数,下面是二元运算符的实例。我们平常使用的加运算符( + )、减运算符( - )、乘运算符( * )和除运算符( / )都属于二元运算符。就像加(+)运算符。

下面的实例演示了如何重载加运算符( + ),减运算符(-)和乘运算符(*)。类似地,您也可以尝试重载除运算符( / )。

#include <iostream>

using namespace std;

class Box
{
public:
    Box() { }

    Box(double len, double bre, double hei)
    {
        length = len;
        breadth = bre;
        height = hei;
    }

    double GetVolume(void)
    {
        return length * breadth * height;
    }

    //重载 + 运算符,用于把两个 Box 对象相加
    Box operator +(const Box &another)
    {
        Box box;

        box.length = this->length + another.length;
        box.breadth = this->breadth + another.breadth;
        box.height = this->height + another.height;

        return box;
    }

    //非成员函数的方式重载运算符 -
    /* 当 2 个对象相减时是没有顺序要求的,但要重载 - 让其与一个数字相加则有顺序要求,
        可以通过加一个友元函数使另一个顺序的输入合法。*/
    friend Box operator -(const Box &box1, const double f);

    //重载 * 运算符,用于把两个 Box 对象相乘
    Box operator *(const Box &another)
    {
        Box box;

        box.length = this->length * another.length;
        box.breadth = this->breadth * another.breadth;
        box.height = this->height * another.height;

        return box;
    }

private:
    double length;      // 长度
    double breadth;     // 宽度
    double height;      // 高度
};

//非成员函数的方式重载运算符 -
Box operator -(const Box &box1, const double f)
{
    Box box;

    box.length = box1.length - f;
    box.breadth = box1.breadth - f;
    box.height = box1.height - f;

    return box;
}

int main() 
{
    Box box1(1.0, 1.0, 1.0);
    Box box2(2.0, 2.0, 2.0);
    Box box3(0, 0, 0);
    Box box4(0, 0, 0);
    Box box5(0, 0, 0);

    // Box1 的体积
    cout << "Volume of box1 : " << box1.GetVolume() << endl;
    // Box2 的体积
    cout << "Volume of box2 : " << box2.GetVolume() << endl;

    // 把两个对象相加,得到 box3
    box3 = box1 + box2; //相当月box3=box1.operator+(box2); 
    // box3 的体积
    cout << "Volume of box3 : " << box3.GetVolume() << endl;

    // 把两个对象相减,得到 box3
    box4 = box1 - 3;
    // box3 的体积
    cout << "Volume of box4 : " << box4.GetVolume() << endl;

    // 把两个对象相乘,得到 box5
    box5 = box1 * box2;
    // box5 的体积
    cout << "Volume of box5 : " << box5.GetVolume() << endl;

    return 0;
}

/*
输出结果:

Volume of box1 : 1
Volume of box2 : 8
Volume of box3 : 27
Volume of box4 : -8
Volume of box5 : 8
*/



3 关系运算符重载

#include "pch.h"
#include <iostream>

using namespace std;

class Distance
{
public:
    Distance(int t_len)
    {
        len = t_len;
    }

    bool operator <(const Distance &another)
    {
        if (len < another.len)
            return true;
    }

private:
    int len;
};

// 程序的主函数
int main()
{
    Distance distant1 = 5;
    Distance distant2 = 10;

    if (distant1 < distant2)
        cout << "distant1 < distant2" << endl;
    else
        cout << "distant1 >= distant2" << endl;

    return 0;
}

/*
输出结果:

distant1 < distant2
*/



4 输入/输出运算符重载

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

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

下面的实例演示了如何重载提取运算符 >> 和插入运算符 <<。

#include <iostream>
#include <string>

using namespace std;

class Student
{
public:
    Student()
    {
        age = 0;
        name = " ";
    }

    Student(int t_age, string t_name)
    {
        age = t_age;
        name = t_name;
    }

    friend ostream &operator <<(ostream &out, const Student &stu)
    {
        out << "age:" << stu.age << " name:" << stu.name << endl;
        return out;
    }

    friend istream &operator >>(istream &in, Student &stu)
    {
        in >> stu.age >> stu.name;
        return in;
    }

private:
    int age;
    string name;
};

// 程序的主函数
int main()
{
    Student stu1(18,"xiaoMing");
    Student stu2;

    cin >> stu2;
    cout << "stu1:" << stu1 << endl;
    cout << "stu2:" << stu2 << endl;

    return 0;
}

/*
输出结果:

20
feng
stu1:age:18 name:xiaoMing

stu2:age:20 name:feng
*/



5 赋值运算符重载

就像其他运算符一样,您可以重载赋值运算符( = ),用于创建一个对象,比如拷贝构造函数。

下面的实例演示了如何重载赋值运算符。

#include <iostream>
#include <string>

using namespace std;

class Student
{
public:
    Student()
    {
        age = 0;
        name = " ";
    }

    Student(int t_age, string t_name)
    {
        age = t_age;
        name = t_name;
    }

    void print()
    {
        cout << "stu:" << age << " " << name << endl;
    }

    void operator =(const Student &stu)
    {
        age = stu.age;
        name = stu.name;
    }

private:
    int age;
    string name;
};

// 程序的主函数
int main()
{
    Student stu1(18,"xiaoMing");
    Student stu2;

    stu2 = stu1;
    stu2.print();

    return 0;
}

/*
输出结果:

stu:18 xiaoMing
*/



6 函数调用运算符 () 重载

函数调用运算符 () 可以被重载用于类的对象。当重载 () 时,您不是创造了一种新的调用函数的方式,相反地,这是创建一个可以传递任意数目参数的运算符函数。

下面的实例演示了如何重载函数调用运算符 ()。

#include <iostream>

using namespace std;
 
class Distance
{
   private:
      int feet;             // 0 到无穷
      int inches;           // 0 到 12
   public:
      // 所需的构造函数
      Distance(){
         feet = 0;
         inches = 0;
      }
      Distance(int f, int i){
         feet = f;
         inches = i;
      }
      // 重载函数调用运算符
      Distance operator()(int a, int b, int c)
      {
         Distance D;
         // 进行随机计算
         D.feet = a + c + 10;
         D.inches = b + c + 100 ;
         return D;
      }
      // 显示距离的方法
      void displayDistance()
      {
         cout << "F: " << feet <<  " I:" <<  inches << endl;
      }
      
};

int main()
{
   Distance D1(11, 10), D2;

   cout << "First Distance : "; 
   D1.displayDistance();

   D2 = D1(10, 10, 10); // invoke operator()
   cout << "Second Distance :"; 
   D2.displayDistance();

   return 0;
}



7 下标运算符 [] 重载

下标操作符 [] 通常用于访问数组元素。重载该运算符用于增强操作 C++ 数组的功能。

下面的实例演示了如何重载下标运算符 []。

#include <iostream>

using namespace std;

const int SIZE = 10;

class safearay
{
private:
    int arr[SIZE];
public:
    safearay()
    {
        register int i;
        for (i = 0; i < SIZE; i++)
        {
            arr[i] = i;
        }
    }
    int& operator[](int i)
    {
        if (i > SIZE)
        {
            cout << "索引超过最大值" << endl;
            // 返回第一个元素
            return arr[0];
        }
        return arr[i];
    }
};
int main()
{
    safearay A;

    cout << "A[2] 的值为 : " << A[2] << endl;
    cout << "A[5] 的值为 : " << A[5] << endl;
    cout << "A[12] 的值为 : " << A[12] << endl;

    return 0;
}

/*
输出结果:

A[2] 的值为 : 2
A[5] 的值为 : 5
索引超过最大值
A[12] 的值为 : 0
*/



8 类成员访问运算符 -> 重载

类成员访问运算符( -> )可以被重载,但它较为麻烦。它被定义用于为一个类赋予"指针"行为。运算符 -> 必须是一个成员函数。如果使用了 -> 运算符,返回类型必须是指针或者是类的对象。

运算符 -> 通常与指针引用运算符 * 结合使用,用于实现"智能指针"的功能。这些指针是行为与正常指针相似的对象,唯一不同的是,当您通过指针访问对象时,它们会执行其他的任务。比如,当指针销毁时,或者当指针指向另一个对象时,会自动删除对象。

间接引用运算符 -> 可被定义为一个一元后缀运算符。也就是说,给出一个类:

class Ptr{
   //...
   X * operator->();
};

Ptr 的对象可用于访问类 X 的成员,使用方式与指针的用法十分相似。例如:

void f(Ptr p )
{
   p->m = 10 ; // (p.operator->())->m = 10
}

语句 p->m 被解释为 (p.operator->())->m。同样地,下面的实例演示了如何重载类成员访问运算符 ->。

#include "pch.h"
#include <iostream>
#include <vector>
using namespace std;

// 假设一个实际的类
class Obj {
    static int i, j;
public:
    void f() const { cout << i++ << endl; }
    void g() const { cout << j++ << endl; }
};

// 静态成员定义
int Obj::i = 10;
int Obj::j = 12;

// 为上面的类实现一个容器
class ObjContainer {
    vector<Obj*> a;
public:
    void add(Obj* obj)
    {
        a.push_back(obj);  // 调用向量的标准方法
    }
    friend class SmartPointer;
};

// 实现智能指针,用于访问类 Obj 的成员
class SmartPointer {
    ObjContainer oc;
    int index;
public:
    SmartPointer(ObjContainer& objc)
    {
        oc = objc;
        index = 0;
    }
    // 返回值表示列表结束
    bool operator++() // 前缀版本
    {
        if (index >= oc.a.size() - 1) return false;
        if (oc.a[++index] == 0) return false;
        return true;
    }
    bool operator++(int) // 后缀版本
    {
        return operator++();
    }
    // 重载运算符 ->
    Obj* operator->() const
    {
        if (!oc.a[index])
        {
            cout << "Zero value";
            return (Obj*)0;
        }
        return oc.a[index];
    }
};

int main() {
    const int sz = 10;
    Obj o[sz];
    ObjContainer oc;
    for (int i = 0; i < sz; i++)
    {
        oc.add(&o[i]);
    }
    SmartPointer sp(oc); // 创建一个迭代器
    do {
        sp->f(); // 智能指针调用
        sp->g();
    } while (sp++);
    return 0;
}

/*
输出结果:

10
12
11
13
12
14
13
15
14
16
15
17
16
18
17
19
18
20
19
21
*/



注意:

  • 1、运算重载符不可以改变语法结构。
  • 2、运算重载符不可以改变操作数的个数。
  • 3、运算重载符不可以改变优先级。
  • 4、运算重载符不可以改变结合性。