9/2作业

六、运算符重载

自定义的类中,一般是不支持运算符的,如果对于该类的对象,非要使用相关运算符,需要进行将运算符进行重载

运算符重载的好处:可以使得程序代码更加优雅、好看、简洁、高效

6.1 运算符种类

单、算、移、关、逻辑、条件表达式、赋值、逗号

6.2 运算符重载的定义

运算符重载,也是实现泛型编程的一种,能够实现“一符多用”,可以使用运算符重载来完成对象与对象之间的符号运算

6.3 运算符重载函数

1> 函数名:统一为 operator# //这里的#表示要被重载的运算符

2> 参数:根据运算符的功能和位置而定,全局函数版双目运算符参数有两个,成员函数版双目运算符参数一个

全局函数版,单目运算符参数有一个,成员函数版单目运算符参数无

3> 返回值:根据运算符的特征而定

6.4 调用原则和调用机制

调用时机:当对象使用该运算符时,系统自动调用运算符重载函数

调用机制:左调右参原则,运算符左侧是函数的调用者,运算符右侧是函数的参数

例如:a = b; //a.operator=(b);

s1 + s2; //s1.operator+(s2);

6.5 运算符重载函数的格式

1> 任意一个运算符重载都有两个版本:成员函数版和全局函数版

2> 实际使用过程中,只实现一种重载的情况,常用成员函数版

6.6 算术运算符

1> 种类:+、-、*、/、%

2> 算术运算符属于双目运算符,格式:L # R; L表示左操作数,#表示运算符号,R表示右操作数

3> 左操作(L):既可以是左值也可以是右值

右操作数(R):既可以是左值也可以是右值

运算结果:只能是右值

4> 格式:

        成员函数版:const 类名 operator#(const 类名 &R) const

        解析:第一个const保护运算结果不被修改

                  第二个const保护右操作数在运算过程中不被修改

                  第三个const保护左操作数自身在运算过程中不被修改

全局函数版:const 类名 operator#(const 类名 &L,const 类名 &R)

6.7 赋值类运算符重载

1> 种类:=、+=、-=、*=、/=、%=

2> 赋值运算符属于双目运算符,格式:L # R; L表示左操作数,#表示运算符号,R表示右操作数

3> 左操作(L):只能是左值

右操作数(R):既可以是左值也可以是右值

运算结果:左操作数自身的引用

4> 格式:

成员函数版:类名 & operator#(const 类名 &R)

全局函数版:类名 & operator#( 类名 &L,const 类名 &R)

6.8 关系运算符重载函数

1> 种类:>、=、

2> 关系运算符属于双目运算符,格式:L # R; L表示左操作数,#表示运算符号,R表示右操作数

3> 左操作(L):既可以是左值也可以是右值

右操作数(R):既可以是左值也可以是右值

运算结果:bool类型的右值

4> 格式:

成员函数版:const bool operator#(const 类名 &R) const

全局函数版:const bool operator#(const 类名 &L,const 类名 &R)

6.9 单目运算符

1> 种类:-(负号) 、!

2> 格式:# R; #表示运算符号,R表示右操作数

3> 操作数(R):既可以是左值也可以是右值

运算结果:同类的一个右值

4> 格式:

成员函数版:const 类名 operator#() const

全局函数版:const bool operator#(const 类名 &R)

6.10 自增自减运算

1> 种类:++ 、 --

2> 格式:# R; #表示运算符号,R表示右操作数

3> 操作数(R):只能是左值

运算结果:

前置:是自身的引用
    成员格式:类名 &operator#()
    全局格式:类名 &operator#(类名 &R)
后置:临时值
    成员格式:类名 operator#(int)
    全局格式:类名 operator#(类名 &R, int)

6.11 插入和提取运算符重载

1> 插入和提取运算符的来源

namespace std
{
    ostream cout;
    istream cin;
}

2> 由于运算符重载函数的调用遵循左调有参原则,所以,自定义类的重载函数,需要流对象和自定义类的对象

例如:cout

3> 此时,原则上来说,需要在流所在的类对象中,写成员函数,难度较大,所以,对于该类的重载函数,我们只能实现全局函数版,将该函数作为类的友元函数

4> 由于不需要使用istream和ostream类中的私有成员,所以,全局函数版的重载函数不需要在流对应的类中,声明友元函数

如果,重载函数中,需要使用自定义的类中的私有成员,则需要在自定义类中,声明该全局函数为友元函数

5> 格式:

        ostream& operator

        istream& operator>>(istream &L, 类名 &R)

6.12 类型转换运算符

1> 有时,需要将类对象进行强制类型转换为其他数据类型,此时就需要在类体内提供类型转换运算符重载函数

2> 定义格式: operator 要转换的类型() { 返回对应类型的数据 }

6.13 函数对象(仿函数)

1> 本质上就是重载 () 运算符,由于调用时,跟函数调用一样,所以称为仿函数

2> 定义格式: operator() (参数列表) {函数体内容}

3> 调用格式:对象名(实参名); //对象 . operator() (实参名)

6.14 运算符重载的限制

不能重载运算符 ::(作用域解析)、.(成员访问)、.*(通过成员指针的成员访问)及 ?:(三元条件)。
不能创建新运算符,例如 **、<> 或 &|。
运算符 && 与 || 的重载失去短路求值。
重载的运算符 -> 必须要么返回裸指针,要么(按引用或值)返回同样重载了运算符 -> 的对象。
不可能更改运算符的优先级、结合方向或操作数的数量。
&&、|| 和 ,(逗号)在被重载时失去其特殊的定序性质,并且即使不使用函数调用记法,也表现为与常规的函数调用相似。
(C++17 前)

#include <iostream>


using namespace std;


//定义一个复数类: 由实部和虚部组成的一个数据  a + bi
class Complax
{
private:
    int real;            //实部
    int vir;             //虚部


public:
    Complax() {cout<<"无参构造"<<endl;}            //无参构造
    Complax(int r, int v):real(r),vir(v) {cout<<"有参构造"<<endl;}   //有参构造
    ~Complax() {cout<<"析构函数"<<endl;}           //析构函数
    //定义输出函数
    void show()
    {
        if(vir > 0)
        {
            cout<<real << "+"<<vir<<"i"<<endl;
        }else
        {
            cout<<real << vir<<"i"<<endl;
        }
    }


    //定义成员函数版完成两个复数相加:实部+实部   虚部+虚部
    const Complax operator+(const Complax &R)const
    {
        Complax temp;       //无参构造
        temp.real = this->real + R.real;
        temp.vir = this->vir + R.vir;


        return temp;
    }


    //实现全局函数版,需要将全局函数版设置成友元函数
    friend const Complax operator-(const Complax &L, const Complax &R);


    //成员函数版完成加等于运算符重载: 实部 += 实部   虚部+=虚部
    Complax &operator+=(const Complax &R)
    {
        this->real += R.real;
        this->vir += R.vir;


        return *this;
    }


    //定义成员函数版完成关系运算符重载: 实部>实部   && 虚部>虚部
     bool operator>(const Complax &R)const
    {
        return this->real>R.real && this->vir>R.vir;
    }


     //定义成员函数版完成负号运算符重载:   实部 = -实部    虚部=-虚部
     const Complax operator-()const
     {
         Complax temp;
         temp.real = -this->real;
         temp.vir = -this->vir;


         return temp;
     }


     //定义成员函数版完成++前置运算: 实部 +=1   虚部+=1
     Complax &operator++()
     {
         this->real++;
         this->vir++;


         return *this;
     }


     //定义成员函数版完成++后置运算: 实部 +=1   虚部+=1
     Complax operator++(int)        //这里括号中的int仅仅起到占位作用,使用的是哑元
     {
         Complax temp;
         temp.real = this->real++;
         temp.vir = this->vir++;


         return temp;
     }


     //将插入运算符重载函数,设置成友元函数
     friend ostream & operator<<(ostream &L, const Complax &R);


     //定义类型转换运算符重载函数
     operator int()
     {
         return this->real + this->vir;
     }


     //重载小括号运算符,完成仿函数
     void operator()(string msg)
     {
         cout << msg<< endl;
     }




};


//定义全局函数版完成两个复数相减
const Complax operator-(const Complax &L, const Complax &R)
{
    Complax temp;       //无参构造
    temp.real = L.real - R.real;
    temp.vir = R.vir - R.vir;


    return temp;
}


//自定义插入运算符重载函数:只能实现全局函数版
ostream & operator<<(ostream &L, const Complax &R)
{
    //输出内容
    if(R.vir > 0)
    {
        L<<R.real << "+"<<R.vir<<"i"<<endl;
    }else
    {
        L<<R.real << R.vir<<"i"<<endl;
    }


    //返回左操作数自身
    return L;
}


int main()
{
    Complax c1(5,7);           //有参构造
    c1.show();                //5+7i
    Complax c2(3, -2);            //有参构造
    c2.show();                    //3-2i
    cout<<"**************************************"<<endl;


    Complax c3 = c1+c2;       //调用加号 运算符重载函数
    c3.show();                //8+5i
    cout<<"**************************************"<<endl;


    (c1+=c2) += c3;          //调用加等于运算符重载函数
    c1.show();            //16+10i
    c2.show();             //3-2i
    c3.show();              //8+5i
    cout<<"**************************************"<<endl;


    if(c1>c2)                      //调用关系运算符重载函数
    {
        cout<<"yes"<<endl;
    }else
    {
        cout<<"no"<<endl;
    }
    cout<<"**************************************"<<endl;


    Complax c4 = -c1;          //调用负号运算符重载
    c4.show();                  // -16 - 10i
    cout<<"**************************************"<<endl;


    /*
    int num = 5;
    num++++;          //后置自增后,返回结果是一个右值
    ++++num;          //前置自增后,返回的结果是一个左值
    */
    Complax c5 = ++++c1;            //前置自增,先加再用
    c5.show();         //18+12i
    c1.show();         //18+12i


    cout<<"**************************************"<<endl;


    Complax c6 = c1++;        //后置自增,先用再加
    c6.show();             //18+12i
    c1.show();             //19+13i
    cout<<"**************************************"<<endl;


    cout<<c1 <<c2;          //cout.operator<<(c1);
    cout<<"**************************************"<<endl;


    int num = (int)c1;         //调用强制类型转换函数
    cout<<"num = "<<num<<endl;      //32
    cout<<"**************************************"<<endl;


    //仿函数的调用
    c1("hello world");
    cout<<"**************************************"<<endl;




    return 0;
}

作业:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值