• 6、多态性-2、运算符重载


    1、c++中预定义的运算符的操作对象只能是基本数据类型,实际上,对于很多用户自定义类型(如类),也需要有类似的运算操作。例如,下面的程序段定义了一个复数类:

    class complex

    {

      public:

        complex(double r=0.0,double i=0.0){real=r;imag=i;}

        void display();

      private:

        double real;

        double imag;

    };

    于是我们可以这样声明复数类的对象:complex a(10,20),b(5,8);

    那么如何对这两个对象进行加法运算呢?我们当然希望能使用"+"运算符,写出表达式"a+b",但是编译的时候会出错,因为编译器不知道该如何完成这个加法。这时候就需要我们自己编写程序来说明"+"在作用于complex类对象时,该实现什么样的功能,这就是运算符重载

    运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用于不同类型的数据时导致不同的行为其实质就是函数重载,在实现过程中首先把指定的运算表达式转化为对运算符函数的调用,运算对象转化为运算符函数的实参,然后根据实参的类型来确定需要调用的函数,这个过程是在编译过程中完成的

    2、运算符重载的规则

    运算符重载的规则如下:

     1)c++中的运算符除了少数几个之外,全部可以重载,而且只能重载c++中已经有的运算符

    2)重载之后运算符的优先级和结合性都不会改变

    3)运算符重载是针对新类型数据的实际需要,对原有运算符进行适当的改造。

    不能重载的运算符有以下5个:他们是类属关系运算符"."、成员指针运算符".*"、作用域分辨符"::"、sizeof运算符和三目运算符"?:"。

    运算符的重载形式有两种,重载为类的成员函数和重载为类的友元函数

    运算符重载为类的成员函数的一般语法形式为:

    函数类型 operator 运算符(形参表)

    {

      函数体;

    }

    运算符重载为类的友元函数,可以在类中声明友元函数的原型,在类外实现,也可以在类体中写成如下内联形式:

    friend 函数类型 operator 运算符(形参表)

    {

      函数体;

    }

    函数类型指定了重载运算符的返回值类型,也就是运算结果类型

    operator是定义运算符重载函数的关键字;

    运算符即是要重载的运算符名称,必须是c++中可重载的运算符。

    形参表中给出重载运算符所需要的参数和类型

    当运算符重载为类的成员函数时,函数的参数个数比原来的操作数个数要少一个(后置“++”、“--”除外);

    重载为类的友元函数时,参数个数与原操作数个数相同。

    原因是重载为类的成员函数时,如果某个对象使用重载了的成员函数,自身的数据可以直接访问,就不需要再放在参数表中进行传递,少了的操作数就是该对象本身。而重载为友元函数时,友元函数对某个对象的数据进行操作,就必须通过该对象的名称来进行,因此使用到的参数都要进行传递。

    3、运算符重载为成员函数

    运算符重载实质上就是函数重载,重载为成员函数,它就可以自由地访问本类的数据成员

    在实际使用时,总是通过该类的某个对象来访问重载的运算符,如果是双目运算符,一个操作数是对象本身的数据,由this指针指出,另一个操作符则需要通过运算符重载函数的参数表来传递;如果是单目运算符操作数由对象的this指针指出,就不再需要任何参数。

    对于双目运算符B,如果要重载为类的成员函数,使之能够实现表达式oprd1 B oprd2,其中oprd1为A类的对象,则应当把B重载为A类的成员函数,该函数只有一个形参,形参的类型是oprd2所属的类型。经过重载之后,表达式oprd1 B oprd2就相当于函数调用oprd1.operator B(oprd2)。

    对于前置单目运算符U,如“-”(负号)等,如果要重载为类的成员函数,用来实现表达式U oprd,其中oprd为A类的对象,则U应当重载为A类的成员函数,函数没有形参,经过重载之后,表达式U oprd相当于函数调用oprd.operator U()。

    再来看后置运算符“++”和“--”,如果要将他们重载为类的成员函数,用来实现表达式oprd++或oprd--,其中oprd为A类的对象,那么运算符就应当重载为A类的成员函数,这时函数要带有一个整型(int)形参。重载之后,表达式oprd++和oprd--就相当于函数调用oprd.operator ++(0)和oprd.operator --(0),这里的int类型参数在运算中不起任何作用,只是用于区别后置++、--与前置++、--。

    例子:

    复数类加减法运算重载---成员函数形式;它是一个双目运算符重载为成员函数的实例。运算符的两个操作数都是复数类的对象,因此,可以把“+”、“-”运算符重载为复数类的成员函数,重载函数只有一个形参。类型同样也是复数类对象。

    #include<iostream>
    using namespace std;
    class complex //复数类定义
    {
    public:
    complex(double r=0.0,double i=0.0){real=r;imag=i;}//构造函数
    complex operator + (complex c2);//运算符+重载成员函数
    complex operator - (complex c2);//运算符-重载成员函数
    void display();//输出复数
    private:
    double real;//复数实部
    double imag;//复数虚部
    };

    complex complex::operator+(complex c2)//重载运算符函数实现
    {
    return complex(real+c2.real,imag+c2.imag);//创建一个临时无名对象作为返回值,这里的real和imag是本类或者说是本类的对象中的数据
    }
    complex complex::operator-(complex c2)//重载运算符函数实现
    {
    return complex(real-c2.real,imag-c2.imag);//创建一个临时无名对象作为返回值
    }
    void complex::display()
    {
    cout<<"("<<real<<","<<imag<<")"<<endl;
    }
    int main()
    {
    complex c1(5,4),c2(2,10),c3;//声明复数类的对象
    cout<<"c1=";c1.display();
    cout<<"c2=";c2.display();
    c3=c1-c2;//使用重载运算符完成复数减法
    cout<<"c3=c1-c2=";
    c3.display();
    c3=c1+c2;//使用重载运算符完成复数加法
    cout<<"c3=c1+c2=";
    c3.display();
    getchar();
    }

     输出结果:

    可以看出,运算符“+、-”原有的功能都不改变,对整型数、浮点数等基本类型数据的运算仍然遵循c++预定义的规则,同时添加了新的针对复数运算的功能。“+”这个运算符,作用于不同的对象上,就会导致完全不同的操作行为,具有了更为广泛的多态特征

    在本例中使用了一个临时的无名对象作为返回值:return complex(c.real,c.imag);

    这表面上看起来像是“对构造函数的调用”,但其实并非如此,这是临时对象语法,它的含义是:“创建一个临时对象并返回它”。当然,也可以按如下形式返回函数值:

    complex complex::operator+(complex c2)

    {

      complex c(real+c2.real,imag+c2.imag);

      return c;

    }

    这两种方法的执行效率是完全不同的,后者的执行过程是这样的:创建一个局部对象c(这时会调用构造函数),执行return语句时会调用拷贝构造函数,将c的值拷贝到主调函数中的一个无名临时对象中。当函数operator+结束时,会调用析构函数析构对象c,然后,c消亡,前一种方法的效率就高多了,是直接将一个无名临时对象创建到主调函数中。

    例子2:将单目运算符“++”重载为成员函数形式

    使用时钟类的例子,单目运算符前置++和后置++的操作数是时钟类的对象,可以把这些运算符重载为时钟类的成员函数,对于前置单目运算符,重载函数没有形参,对于后置单目运算符,重载函数有一个int型形参。

    #include<iostream>
    using namespace std;
    class Clock //时钟类定义
    {
    public:
    Clock(int NewH=0,int NewM=0,int NewS=0);
    void ShowTime();
    Clock& operator ++();//前置单目运算符重载
    Clock operator ++(int);//后置单目运算符重载
    private:
    int Hour,Minute,Second;
    };
    Clock::Clock(int NewH,int NewM,int NewS)//构造函数
    {
    if(0<=NewH&&NewH<24&&0<=NewM&&NewM<60&&0<=NewS&&NewS<60)
    {
    Hour = NewH;
    Minute=NewM;
    Second = NewS;
    }
    else
    {
    cout<<"Time error!"<<endl;
    }
    }
    void Clock::ShowTime()
    {
    cout<<Hour<<":"<<Minute<<":"<<Second<<endl;
    }
    Clock& Clock::operator++() //前置单目运算符重载函数
    {
    Second++;
    if(Second>=60)
    {
    Second=Second-60;
    Minute++;
    if(Minute>=60)
    {
    Minute=Minute-60;
    Hour++;
    Hour=Hour%24;
    }
    }
    return *this;//因为是处理的本对象的数据,所示使用this
    }
    Clock Clock::operator++(int)//后置单目运算符重载,注意形参表中的整型参数
    {
    Clock old=*this;
    ++(*this);
    return old;
    }


    int main()
    {
    Clock myClock(23,59,59);
    cout<<"First time output:";
    myClock.ShowTime();
    cout<<"Show myClock++:";
    (myClock++).ShowTime();
    cout<<"show ++myClock:";
    (++myClock).ShowTime();
    getchar();
    }

    4、运算符重载为友元函数

    运算符也可以重载为类的友元函数,这样,它就可以自由地访问该类的任何数据成员。这时,运算所需要的操作数都需要通过函数的形参表来传递,在形参表中形参从左到右的顺序就是运算符操作数的顺序。

    对于双目运算符B,如果它的一个操作数为A类的对象,就可以将B重载为A类的友元函数,该函数有两个形参,其中一个形参的类型是A类。经过重载之后,表达式oprd1 B oprd2就相当于函数调用operator B(oprd1,oprd2)

    对于前置单目运算符U,如“-”(负号)等,如果要实现表达式U oprd,其中oprd为A类的对象,则U可以重载为A类的友元函数,函数的形参为A类的对象oprd。经过重载之后,表达式U oprd相当于函数调用operator U(oprd)

    对于后置运算符++和--,如果要实现表达式oprd++或oprd--,其中oprd为A类的对象,那么运算符就可以重载为A类的友元函数,这时函数的形参有两个,一个是A类对象oprd,另一个是int类型形参。第二个参数是用于与前置运算符函数相区别的。重载之后,表达式oprd++和oprd--就相当于函数调用operator ++(oprd,0)和operator --(oprd,0)

    例子:将复数类加减法运算重载为友元函数形式。,本例将“+”,“-”重载为复数类的友元函数,使之实现复数加减法。

    #include<iostream>
    using namespace std;
    class complex
    {
    public:
    complex(double r=0.0,double i=0.0){real=r;imag=i;}
    friend complex operator + (complex c1,complex c2);//运算符+重载友元函数
    friend complex operator - (complex c1,complex c2);
    void display();
    private:
    double real;
    double imag;
    };
    void complex::display()
    {
    cout<<"("<<real<<","<<imag<<")"<<endl;
    }
    complex operator +(complex c1,complex c2)
    {
    return complex(c2.real+c1.real,c2.imag+c1.imag);
    }
    complex operator -(complex c1,complex c2)
    {
    return complex(c1.real-c2.real,c1.imag-c2.imag);
    }
    int main()
    {
    complex c1(5,4),c2(2,10),c3;
    cout<<"c1=";c1.display();
    cout<<"c2=";c2.display();
    c3=c1-c2;//使用重载运算符
    cout<<"c3=c1-c2=";
    c3.display();
    c3=c1+c2;//使用重载运算符
    cout<<"c3=c1+c2=";
    c3.display();
    getchar();
    }

     

  • 相关阅读:
    Cassandra 分页 读取数据
    cassandra高级操作之索引、排序以及分页
    cassandra 可视化工具
    SpringBoot集成Cassandra参考文章
    022 android studio 首次启动时默认的sdk安装路径
    021 Gradle's dependency cache may be corrupt (this sometimes occurs after a network connection timeout)
    020 本机Gradle目录 Could not find com.android.tools.build:gradle:4.1
    019 Android Studio打开XML文件Design显示Waiting for build to finish
    018 Could not download espresso-core-3.2.0.aar (androidx.test.espresso:espresso-core:3.2.0)
    017 Android Studio is using the following JDK location when running Gradle:
  • 原文地址:https://www.cnblogs.com/gary-guo/p/6247551.html
Copyright © 2020-2023  润新知