【C++】C++中的运算符重载

发布于:2025-07-21 ⋅ 阅读:(20) ⋅ 点赞:(0)

1.概念:

C++提供了一种扩展运算符号作用范围的机制
比如: 正常C/C++的语法 加法运算只能用于整数,小数,字符
但是有了运算符重载这种机制,程序员可以实现类的对象相加(任何数据类型均可以相加)

示例代码:认识运算符重载
#include <iostream>

/*
    运算符重载的基本语法规则
    让两只猫对象可以相加,规定猫对象相加就是把年龄相加 
    运算符重载总结成公式:
         返回值类型 operator运算符号(形参)
         {
            // code
         }
    总结:运算符重载本质就是个函数,这个函数有固定写法(名字必须叫做operator运算符号)
*/

class Cat {
public:
    Cat(int _age) : age(_age) {}
    /*
        问题1:如何确定返回值类型和形参?
        答案:依据运算表达式反推
              int result=c1+c2;   //c1+c2是两个猫对象相加,返回值类型是int,形参是Cat &cat
              转换成函数调用的写法
              int result=c1.operator+(c2) //函数调用的思维来写
    */

    //猫类里面重载加法运算,让猫具备做加法的能力
    int operator+(Cat &cat){
        return this->age + cat.age;
    }
private:
    int age;
};

int main(int argc, char const *argv[])
{
    Cat cat1(3), cat2(5);
    // int result = cat1 + cat2;  // 等同于 cat1.operator+(cat2);
    int result = cat1.operator+(cat2);
    std::cout << "cat1 + cat2 is: " << result << std::endl;  // cat1 + cat2 is: 8
    
    Cat cat3 = 8 + cat2; // 
    std::cout << "8 + cat2 is: " << cat3.age << std::endl;
    return 0;
}

2.实现方法:

2.1 运算符重载的本质:

就是程序员自定义一个函数,通过该函数实现运算符功能的扩展

返回值 operator运算符号(参数)
{

}

2.2 运算符重载函数的特点:

2.2.1 统一叫做 operator运算符号

	operator+
	operator-
	operator>
	operator++
	operator==
	operator[]

2.2.2 运算符重载有两种形式

  • 把运算符重载为类的成员函数(隐含着有this指针,函数的参数可以少一个)
  • 把运算符重载为类的友元函数(没有this指针,函数的参数必须完整)

\quad 正常情况下,运算符重载为形式1或者形式2都是没有问题,但是有些情况(不能通过对象来调用)只能用形式2
如:

Rect r2=5+r1; 
思考步骤:
   第一步:我知道要重载加法,但是还不知道是重载为友元函数,还是成员函数合适,还是两种都合适
   第二步:Rect r2=5.operator+(c1); //成员函数的理解,错误的,5不是Rect的对象 
                Rect r2=operator+(5,c1); //友元函数的理解,正确了

运算符重载思考思维:

示例代码:把运算符重载作为类的成员函数

在这里插入图片描述

#include <iostream>

/*
    运算符重载为类的成员函数
*/

class Cat {
public:
    Cat(int _age = 0) : age(_age) {}

    //猫类里面重载加法运算,让猫具备做加法的能力
    // int operator+(Cat &cat){
    //     return this->age + cat.age;
    // }
    // int operator+(int num){

    //  return this->age + num;
    // }

    Cat operator+(Cat &cat) {
        Cat temp;
        temp.age = this->age + cat.age;
        return temp;// Cat(this->age + cat.age)
    }

    Cat operator+(int n) {
        return Cat(this->age + n);
    }
    void show(){
        std::cout<<"age: "<<this->age<<std::endl;
    }

private:
    int age;
};

int main(int argc, char const *argv[])
{
    Cat cat1(3), cat2(5);
    // 写法1:
    // int result = cat1 + cat2;  // 等同于 cat1.operator+(cat2);
    // int result = cat1.operator+(cat2);
    // std::cout << "cat1 + cat2 is: " << result << std::endl;  // cat1 + cat2 is: 8

    // 写法2:
    // result = cat1 + 100;  // 等同于 cat1.operator+(100);
    // std::cout << "cat1 + 100 is: " << result << std::endl;  // cat1 + 100 is: 103

    // 写法3:
    Cat c = cat1 + cat2;  // 等同于 Cat c = cat1.operator+(cat2);
    c.show();   // age: 8
    // 写法4:
    c = cat1 + 100;  // 等同于 Cat c = cat1.operator+(100);
    c.show();   // age: 103

    return 0;
}

示例代码:运算符重载作为友元函数:
#include <iostream>

/*
    运算符重载两种表现形式:
        形式1:运算符重载作为类的成员函数
        形式2:运算符重载作为类的友元函数
    两者的区别:
        区别1:运算符重载作为类的友元函数,无法通过对象来调用,因此代码中不可以使用this指针
        区别2:运算符重载作为类的友元函数,此时没有当前对象,因此传递参数的时候,参数的个数会多一个

*/

class Cat
{
public:
    Cat(int _age=0)
    {
        age=_age;
    }

    //加法重载作为猫类的成员函数
    // int operator+(Cat &other)
    // {
        // cout<<"加法重载作为猫类的成员函数被调用了"<<endl;
        // return this->age+other.age;
    // } 
    friend int operator+(Cat &c1,Cat &c2);
private:
    int age;
};

//加法重载作为猫类的友元函数
int operator+(Cat &c1,Cat &c2)
{
    std::cout<<"加法重载作为猫类的友元函数被调用了"<<std::endl;
    return c1.age+c2.age;
} 

int main()
{
    Cat c1(5);
    Cat c2(7);
    
    //更加清晰地看到,的确是成员函数
    //int result=c1.operator+(c2);
    //cout<<"c1+c2 is: "<<result<<endl;
    
    //更加清晰地看到,的确是友元函数
    //result=operator+(c1,c2);
    //cout<<"c1+c2 is: "<<result<<endl;
    
    //换成写法1:用人类的思维习惯再来调用
    /*
        这种写法会造成冲突,这种写法跟成员函数、友元函数都能匹配
        因此这种写法下,成员函数或者友元函数只能留下了一个
    */
    int result=c1+c2;
    std::cout<<"c1+c2 is: "<<result<<std::endl;
}

/*
执行结果:
    加法重载作为猫类的友元函数被调用了
    c1+c2 is: 12
*/

2.2.3 运算符重载不可以改变运算符原本的语义(不是语法规定,行业潜规则)

比如: 加法运算--》心中的印象就是相加,把两个东西合并
                    不可以发明新的运算符号 
                    如下运算符号不可以重载
                         ?:    ::     .     sizeof  

3.运算符重载有两种表现形式:

  • 把运算符重载作为类的成员函数
  • 把运算符重载作为类的友元函数(注意写法的差异,不能使用this,不属于类的成员函数)

4.几种特殊的运算符重载

4.1 自增和自减

\quad 后置的自增,自减需要添加int类型的参数(参数必须是int类型,不可以是其他类型,为了跟前置的区分)

示例代码:重载++
#include <iostream>
using namespace std;

/*
    重载++
    自增运算有两种
         a++
         ++a
*/

class Cat
{
public:
    Cat(int _age=0)
    {
        age=_age;
    }
    //搞定后置的++
    /*
        人类的思维习惯
            Cat c2=c1++; 
        函数调用
            Cat c2=c1.operator++(); //成员函数的理解
            Cat c2=operator++(c1);  //友元函数的理解
    */
    //error: no ‘operator++(int)’ declared for postfix ‘++’ 
    //错误原因:后置的++添加int类型的参数为了跟前置的++区别
    Cat operator++(int n)
    {
        cout<<"后置的++重载被调用"<<endl;
        Cat temp;
        //先备份当前对象的值
        temp.age=this->age;
        //当前对象的值自己加1
        this->age+=1;
        return temp;
    }
    Cat operator++()
    {
        cout<<"前置的++重载被调用"<<endl;
        //当前对象的值自己加1
        this->age+=1;
        return *this;
    }
    
    void show()
    {
        cout<<"年龄: "<<age<<endl;
    }
private:
    int age;
};

int main()
{
    Cat c1(5);
    //写法1:后置的++
    // Cat c2=c1++; 
    // c1.show();  //6
    // c2.show();  //5
    
    //前置的++
    // Cat c3=++c1;  
    // c1.show(); //7
    // c3.show(); //7
    
    //写法2:函数调用的写法
    Cat c2=c1.operator++(666);
    c1.show();  //6
    c2.show();  //5
    
    Cat c3=c1.operator++();  
    c1.show();  //7
    c3.show();  //7
}

4.2 <<(插入运算符)和>>(抽取运算符)配合cout和cin

示例代码:理解cout输出原理
#include <iostream>
using namespace std;

/*
    研究cout输出的原理
*/

int main()
{
    int a=456;
    float b=45.6;
    char c='@';
    //写法1:人类的思维习惯
    //cout<<a;
    cout<<a<<b<<c<<endl;
    //写法2:函数调用的写法
    //cout.operator<<(a);
    //偷懒:一次性搞定
    cout.operator<<(a).operator<<(b).operator<<(c).operator<<(endl);
    //分步骤写:方便理解  ostream &:返回ostream的引用,方便连续输出
    ostream &ret1=cout.operator<<(a);
    ostream &ret2=ret1.operator<<(b);
    ostream &ret3=ret2.operator<<(c);
    ret3.operator<<(endl);
}

5.重新认识string和cout以及cin

5.1 string

string str=str1+str2;  //对+重载了
示例代码:string类重载的运算符
#include <iostream>
using namespace std;

/*
    string类重载的运算符
*/

int main()
{
    string str="hello";
    string str1="nihao";
    //重载了中括号,获取下标对应的字符
    cout<<str[1]<<endl;             //e
    cout<<str.operator[](1)<<endl;  //e
    
    //重载了赋值
    // str=str1;
    // str.operator=(str1);
    // cout<<str<<endl;    //nihao
}

5.2 cout


//对<<重载,实现cout<<string的对象
cout<<   //ostream重载了<<符号,实现的效果就是输出
class ostream
{
  pubic:
       ostream &operator<<(参数)  cout<<a<<b; -->cout
       {
               代码;
               return cout;  //关键点,实现连续输出的关键点
       }                        
};

写法一:人类的思维习惯

cout<<a<<b<<endl;  //连续输出

写法二:函数调用的习惯

cout.operator<<(a).operator<<(b).operator<<(endl);

ostream重载左移运算:

在这里插入图片描述

示例代码:cout输出自定义的类对象
#include <iostream>
using namespace std;

/*
    受到cout输出的原理启发,直接打印(输出)结构体,自定义的类对象(猫)
*/

class Cat
{
public:
    Cat(string _name,float _weight,int _age)
    {
        name=_name;
        weight=_weight;
        age=_age;
    }
    //friend void operator<<(ostream &out,Cat &other); //缺陷:无法连续打印
    friend ostream &operator<<(ostream &out,Cat &other); //可以连续打印
private:
    int age;
    string name;
    float weight;
};

//void operator<<(ostream &out,Cat &other)   //缺陷:无法连续打印
ostream &operator<<(ostream &out,Cat &other) //可以连续打印
{
    cout<<"友元函数被调用"<<endl;
    out<<other.name<<endl;
    out<<other.weight<<endl;
    out<<other.age<<endl;
    return out;
}

int main()
{
    Cat c1("旺财",55.8,5);
    Cat c2("来福",56.8,6);
    /*
        人类的思维习惯
           cout<<c1;
           cout<<c1<<c2;
        函数调用
           cout.operator<<(c1)  //成员函数,不正确,ostream这个类里面根本就没有这个版本
           operator<<(cout,c1)  //友元函数,正确

    */
    //人类的思维习惯
    //cout<<c1;
    cout<<c1<<c2;
    //函数调用
    //operator<<(cout,c2);
    //浓缩,不好理解
    operator<<(operator<<(cout,c1),c2);
}

/*
执行结果:
    友元函数被调用
    旺财
    55.8
    5
    友元函数被调用
    来福
    56.8
    6
*/
5.3 cin
cin>>    //istream重载了>>符号,实现的效果就是获取键盘输入
class istream
{
  pubic:
       istream &operator>>(参数)  
       {
               代码;
               return cin;  //关键点,实现连续输入的关键点
       }                        
};

写法一:人类的思维习惯

cin>>a>>b  //连续输入

写法二:函数调用的习惯

cin.operator>>(a).operator>>(b)

istream重载右移运算:

在这里插入图片描述

示例代码:读取键盘输入的数据存放到自定义的类对象
#include <iostream>
using namespace std;

/*
    受到cout输出的原理启发,我想直接打印(输出)结构体,自定义的类对象(猫)
    受到cin输入的原理启发,我想直接读取键盘输入的数据存放到自定义的类对象(猫)
*/

class Cat
{
public:
    friend ostream &operator<<(ostream &out,Cat &other); //可以连续打印
    friend istream &operator>>(istream &in,Cat &other);
private:
    int age;
    string name;
    float weight;
};

ostream &operator<<(ostream &out,Cat &other) //可以连续打印
{
    cout<<"友元函数输出被调用"<<endl;
    out<<other.name<<endl;
    out<<other.weight<<endl;
    out<<other.age<<endl;
    return out;
}

istream &operator>>(istream &in,Cat &other) //可以连续输入
{
    cout<<"友元函数输入被调用"<<endl;
    in>>other.name;
    in>>other.weight;
    in>>other.age;
    return in;
}

int main()
{
    Cat c1;
    Cat c2;
    cout<<"请依次输入猫的姓名,体重,年龄"<<endl;
    /*
        人类的思维习惯
           cin>>c1;
        函数调用
           cin.operator>>(c1)   //成员函数,不正确,istream这个类里面根本就没有这个版本
           operator>>(cin,c1)   //友元函数,正确

    */
    cin>>c1;
    cin>>c2;
    //或者
    //cin>>c1>>c2;
    cout<<c1<<c2;
}

/*
执行结果:
    请依次输入猫的姓名,体重,年龄
    友元函数输入被调用
    xiaoh
    45.6
    5
    友元函数输入被调用
    xiaol
    56.7
    9
    友元函数输出被调用
    xiaoh
    45.6
    5
    友元函数输出被调用
    xiaol
    56.7
    9
*/

练习:

1.实现

Cat c3;
c3=c1+c2;
c3=c1+5;
c3=5+c1;

实现学生结构体可以做加法运算(把学生年龄相加即可)

示例代码:

#include <iostream>
using namespace std;



struct student //披着结构体的外衣 ,其实已经算是一个类
{
    char name[10];
    int age;
    //重载加法运算,作为成员函数
    int operator+(struct student &other)
    {
        cout<<"结构体做加法"<<endl;
        return age+other.age;
    }
};

int main()
{
    struct student stu1={"张三",18};
    struct student stu2={"李四",20};
    cout<<"stu1+stu2 is: "<<stu1+stu2<<endl;  // stu1+stu2 is:38
}

2.两种方式实现运算符重载

猫的对象可以用> < ==比较年龄的大小

if(c1>8)
if(8>c1)
if(c1>c2)

示例代码:

#include <iostream>
using namespace std;

class Cat
{
public:
    Cat(int _age)
    {
        age=_age;
    }
    /*
        if(c1>8)
        if(8>c1)
        if(c1>c2)
    */
    bool operator>(int n)
    {
        if(age>n)
            return true;
        else
            return false;
    }
    bool operator>(Cat &other)
    {
        if(age>other.age)
            return true;
        else
            return false;
    }
    friend bool operator>(int n,Cat &other);
private:
    int age;
}

bool operator>(int n,Cat &other)
{
    if(n>other.age)
        return true;
    else
        return false;
}

int main()
{
    Cat c1(5);
    Cat c2(10);
    if(c1>c2)
        cout<<"大于"<<endl;
    else
        cout<<"小于等于"<<endl;
}

3.定义矩形类,宽,高两个属性

实现:
Rect r4=9+r1+5+r2+6+r3-7; //从左到右,一步步计算
要求:分别用两种重载实现

示例代码:

#include <iostream>
using namespace std;

/*
    Rect  r4=9+r1+5+r2+6+r3-7; 
*/

class Rect
{
public:
    Rect(int _w=0,int _h=0)
    {
        w=_w;
        h=_h;
    }
    Rect operator+(int n)
    {
        cout<<"成员版本的加法重载,带int参数"<<endl;
        Rect temp;
        temp.w=n+w;
        temp.h=n+h;
        return temp;
    }
    Rect operator-(int n)
    {
        cout<<"成员版本的减法重载,带int参数"<<endl;
        Rect temp;
        temp.w=w-n;
        temp.h=h-n;
        return temp;
    }
    Rect operator+(Rect &other)
    {
        cout<<"成员版本的加法重载,带Rect参数"<<endl;
        Rect temp;
        temp.w=w+other.w;
        temp.h=h+other.h;
        return temp;
    }
    void show() const
    {
        cout<<"宽: "<<w<<"  高: "<<h<<endl;
    }
    friend Rect operator+(int n,Rect &other);
private:
    int w;
    int h;
};

Rect operator+(int n,Rect &other)
{
    cout<<"友元版本的加法重载"<<endl;
    Rect temp;
    temp.w=n+other.w;
    temp.h=n+other.h;
    return temp;
}

int main()
{
    Rect r1(10,5);
    Rect r2(25,17);
    Rect r3(40,22);
    Rect r4=9+r1+5+r2+6+r3-7; 
    r4.show();
}

/*
执行结果:
    友元版本的加法重载
    成员版本的加法重载,带int参数
    成员版本的加法重载,带Rect参数
    成员版本的加法重载,带int参数
    成员版本的加法重载,带Rect参数
    成员版本的减法重载,带int参数
    宽: 88  高: 57
*/

网站公告

今日签到

点亮在社区的每一天
去签到