重载运算符

用法

在C++中,重载运算符是一种允许程序员为自定义类型(如类)定义或修改运算符的行为的机制。通过重载运算符,可以使自定义类型的对象像内置类型一样使用运算符进行操作,从而提高代码的可读性和可维护性。

重载运算符 作用

(1) 在C++中,我们希望类对象能够像基本类型对象一样进行基本操作,例如“+”、“-”、“*”、“/”,以及某些其他运算符,如“=”、“()”、“[]”、“<<”、“>>”。然而,默认情况下,类对象并不能自动支持这些运算符的操作。为了使类对象能够正确响应这些运算符,我们必须为其定义或重载运算符的行为。

(2) C++提供了一种机制来定义运算符的行为,即通过使用“operator 运算符”的语法来重载运算符,告诉编译器我们正在重载一个运算符,以便为自定义类型定义特定的操作行为。

重载运算符 注意事项

(1) 我们只能重载 C++已有的运算符,eg: 无法将**这个运算符定义为指数的形式, 因为 C++根本没有**这个运算符

(2) C++重载运算符不能改变运算符的元数,“元数”这个概念就是指一个运算符对应的对象数量,比如“+”必须为“a + b”,也就是说“+”必须有两个对象,那么“+”就是二元运算符。比如“++”运算符,必须写为“a++”,也就是一元运算符;

(3) 重载运算符的技巧:

  • 如果需要调用/修改原对象,运算结果为左值,那么就返回引用;
  • 如果只是访问对象,运算结果为右值,那么就返回值;

(4) 重载运算符有两种主要实现方式:

  • 友元重载,直接定义全局的重载运算符函数,然后再在类中声明为友元函数
  • 成员函数重载,使用类的成员函数重载运算符,第一个参数需要使用this,所以<< >>无法使用此方式重载

(5) =运算符会默认进行重载,如果不需要可以用delete关键字进行修饰。

重载运算符 相关代码

运算符有很多,我们在重载运算符的时候一般按照以下框架去写

一元运算符重载: 
自增,自减 ++ -- 
下标 [] 
调用 () 
输入输出 <<,>>
 
二元运算符重载 
基本运算 +,-,*,/  
赋值 = 
比较 >,<,== 

三元运算符?:,不能重载 

类类型转化运算符:
operator 类型
 
特殊的运算符:new,delete,new[],delete[]
#include <iostream>
#include <vector>


class MyInt
{   
    // 重载 << >> 必须使用友元,因为第一个参数无法用this访问
    friend std::ostream& operator<<(std::ostream& os, const MyInt& t);
    friend std::istream& operator>>(std::istream& is, MyInt& t);
public:
    MyInt() : val(0) {}
    MyInt(int val_) : val(val_) {}

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

    // 重载后缀++
    MyInt operator++(int)
    {
        MyInt tmp = *this;
        val++;
        return tmp;
    }

    // 重载前缀--
    MyInt& operator--()
    {
        --val;
        return *this;
    }

    // 重载后缀--
    MyInt operator--(int)
    {
        MyInt tmp = *this;
        val--;
        return tmp;
    }

    // 重载[]
    int operator[](unsigned i)const
    {
        return a[i];
    }

    // 重载()
    void operator()()const
    {
        std::cout << "call function()" << std::endl;
    }


    // 重载+   -*/都差不多就不写了
    MyInt operator+(const MyInt& t)
    {
        val += t.val;
        return *this;
    }

    // 重载=   注意防止自赋值 然后返回原对象(返回引用) 
    MyInt& operator=(const MyInt& t)
    {
        if (this == &t) return *this;
        val = t.val;
        return *this;
    }

    // 重载 <   >,>=,==这些都差不多
    bool operator<(const MyInt& t)const
    {
        return val < t.val;
    }

    int val;
    std::vector<int> a{ 1, 2, 3, 4, 5 };
};


std::ostream& operator<<(std::ostream& os, const MyInt& t)
{
    os << t.val;
    return os;
}

std::istream& operator>>(std::istream& is, MyInt& t)
{
    is >> t.val;
    return is;
}

int main()
{
    std::cout << "测试 ++" << std::endl;
    MyInt t1(10);
    ++t1;
    std::cout << "expected: t1 = 11 " << "now: t1 = " << t1 << std::endl;
    MyInt t2 = t1++;
    std::cout << "expected: t1 = 12, t2 = 11 " << "now: t1 = " << t1 << " now t2 = " << t2 << std::endl;

    std::cout << "探索 前缀++和后缀++ 区别" << std::endl;
    int a = 3;
    int b = ++(++a);   // 验证 前缀++后的运算结果是左值,可以继续调用,所以重载需要返回引用
    //int c = (a++)++;   // 验证 后缀++后的运算结果是右值,不能继续调用,所以重载是返回值
    int c = a++;
    std::cout << a << " " << b << " " << c << std::endl;
    

    // 测试 --
    std::cout << "测试 --" << std::endl;
    --t1;
    t1--;
    std::cout << "expected: t1 = 10 " << "now: t1 = " << t1 << std::endl;

    // 测试[]
    std::cout << "测试 []" << std::endl;
    std::cout << "expected: t1[1] = 2 " << "now: t1[1] = " << t1[1] << std::endl;

    // 测试()
    std::cout << "测试 ()" << std::endl;
    t1();


    // 测试 << >>
    std::cout << "测试 << >>" << std::endl;
    MyInt t3;
    std::cin >> t3;
    std::cout << t3 << std::endl;


    // 测试 +
    std::cout << "测试 +" << std::endl;
    MyInt t4 = t1 + t2;
    std::cout << t4 << std::endl;

    // 测试=
    std::cout << "测试 =" << std::endl;
    MyInt t5;
    std::cout << t5 << std::endl;
    t5 = t4;
    std::cout << t5 << std::endl;

    // 测试<
    std::cout << "测试 <" << std::endl;
    MyInt t6(10), t7(12);
    std::cout << (t6 < t7) << std::endl;
    //return 0;
}