zoukankan      html  css  js  c++  java
  • 运算符重载

    整理自《面向对象程序设计》

    3.1什么是运算符重载

    为了实现两个Time类对象的加法运算,可以写出如下语句:

    Time t1,t2;//定义时间类对象t1、t2
    t1=Tadd(t1,t2);//调用函数Tadd()计算两个时间的和
    //显然这种调用方式不直观,太繁琐
    t1=t1+t2//使用运算符重载可以直接用加好来实现时间的加法运算

    所谓重载,就是重新赋予新的含义。运算符重载是将系统中已有的运算符赋予不同的意义。使用运算符重载可以使C++的代码更直观、更易懂、更灵活,使得用户自定义的数据类型以一种更方便、更简洁的方式工作。

    由于运算符也是函数,所以在用户自定义的类可以去重载这些函数。运算符重载的方法就是定义一个重载运算符的函数,在需要执行被重载的运算符时,系统就自动调用该函数,以实现相应的运算。

    运算符通常是对类中的私有成员进行操作,故重载运算符应能访问类中的私有成员,所以重载运算符一般采用成员函数或友元函数的形式

    3.2重载运算符的规则

    • 重载运算符可以对运算符做出新的解释,但原有的基本语义不变。
    • 不改变运算符的优先级和结合性。
    • 不改变运算符所需要的操作数,即单目运算符只能重载为单目运算符,不能将单目运算符重载为双目运算符。
    • 不能创建新的运算符,只有系统预定义的运算符才能被重载,除作用域操作符 :: 条件操作符 ? 点操作符 . 指向成员操作的指针操作符 ->*,.*预处理符号:#外 ,其他系统预定义的运算符都可以被重载
    • 重载运算符的函数不能有默认的参数,否则就改变了运算符参数的个数。
    • 重载的运算符必须和用户自定义类型的对象一起使用,其参数至少应该有一个是类对象或类对象的引用
    • 用于类对象的运算符一般必须重载,但有两个例外,运算符 = 和 &,用户不必重载这两个运算符
    • 运算符重载函数可以是类的成员函数,也可以是类的友元函数。对于=、()、[] 和 ->,运算符只能用成员函数的方式进行重载,对于 << 和 >> 运算符必须用友元函数的方式进行重载

    3.3运算符重载函数作为类的成员函数

    可以将运算符重载函数作为类的成员函数,方法是在类中定义一个同名的运算符函数来重载该运算符。

    定义运算符重载函数的格式如下:

    函数类型 operator 运算符名称(形参表){
       函数体;   
    }
    //例如
    Time operator+(time t)

    其中operator+ 为函数名,形参表中是运算符要求的操作数。在定义重载运算符的函数后,可以说函数operator+重载了运算符+。

    【对自定义时间类Time 实现加法操作】

    //Time.h
    class Time {
    private:
        int hour;
        int minute;
        int sec;
    public:
        Time():hour(0), minute(0), sec(0) {};
        Time(int x, int y , int z ) :hour(x), minute(y), sec(z) {};
        Time operator+(Time &t);
        void show();
    };
    //Time.cpp
    #include<iostream>
    using namespace std;
    #include"Time.h"
    
    Time Time::operator+(Time &t) {
        Time mt;
        int jinwei = 0;
        mt.sec = sec + t.sec;
        if (mt.sec >= 60) {
            mt.sec -= 60;
            jinwei = 1;
        }
        mt.minute = minute + t.minute+jinwei;
        jinwei = 0;
        if (mt.minute >= 60) {
            mt.minute -= 60;
            jinwei = 1;
        }
        mt.hour = hour + t.hour + jinwei;
        return mt;
    }
    void Time::show() {
        cout << hour << ":" << minute << ":" << sec;
    }
    //main.cpp
    #include"Time.h"
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    using namespace std;
    int main() {
        Time t1(10, 10, 10), t2(20, 55, 55), t3;
        t3 = t1 + t2;
        t1.show();
        cout << "+";
        t2.show();
        cout << "+";
        cout << "=";
        t3.show();
        cout << endl;
        getchar();
    }
    /*
    10:10:10+20:55:55+=31:6:5
    */
    View Code

    语句t3 = t1 + t2;在编译时变成什么样子呢?由于已经将+运算符重载为Time类的成员函数,这一语句首先编译成通过对象来调用类的成员函数的形式:t3 = t1.operatro+(t2);,在每一个成员函数中都包含一个this指针。这样在执行,C++把它处理为t3=t1.operator+(&t1,t2);即给operator+()函数新增一个参数&t1。对于Time::operator+()函数,C++把它处理为如下形式:

    Time Time::operator+(Time *this, Time &t){
        Time mt;
        int jinwei = 0;
        mt.sec = this->sec + t.sec;
        if (mt.sec >= 60) {
            mt.sec -= 60;
            jinwei = 1;
        }
        mt.minute = this->minute + t.minute + jinwei;
        jinwei = 0;
        if (mt.minute >= 60) {
            mt.minute -= 60;
            jinwei = 1;
        }
        mt.hour = this->hour + t.hour + jinwei;
        return mt;
    }

    【对自定义的时间类Time重载运算符“!”作为其成员函数,用于判断时间对象是否为0】

    //Time.h
    class Time {
    private:
        int hour;
        int minute;
        int sec;
    public:
        Time():hour(0), minute(0), sec(0) {};
        Time(int x, int y , int z ) :hour(x), minute(y), sec(z) {};
        Time operator+(Time &t);
        int operator!();//声明重载的“!”运算符
        void show();
    };
    //Time.cpp
    #include<iostream>
    using namespace std;
    #include"Time.h"
    
    int Time::operator!() {
        if ((hour == 0) && (minute == 0) && (sec == 0)) return 1;
        else return 0;
    }
    Time Time::operator+(Time &t) {
        Time mt;
        int jinwei = 0;
        mt.sec = sec + t.sec;
        if (mt.sec >= 60) {
            mt.sec -= 60;
            jinwei = 1;
        }
        mt.minute = minute + t.minute+jinwei;
        jinwei = 0;
        if (mt.minute >= 60) {
            mt.minute -= 60;
            jinwei = 1;
        }
        mt.hour = hour + t.hour + jinwei;
        return mt;
    }
    void Time::show() {
        cout << hour << ":" << minute << ":" << sec;
    }
    //main.cpp
    #include"Time.h"
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    using namespace std;
    int main() {
        Time t1(10, 10, 10), t2;
        if (!t1) cout << "t1 is 0" << endl;
        else cout << "t1 is not 0" << endl;
        if (!t2) cout << "t2 is 0" << endl;
        else cout << "t2 is not 0" << endl;
        getchar();
    }
    /*
    t1 is not 0
    t2 is 0
    */
    View Code

    3.4运算符重载函数作为类的友元函数

    【为了便于理解,重载函数operator+()不作为Time类的成员函数,而是把重载函数放在类外,作为Time类的友元函数】

    //Time.h
    class Time {
    private:
        int hour;
        int minute;
        int sec;
    public:
        Time():hour(0), minute(0), sec(0) {};
        Time(int x, int y , int z ) :hour(x), minute(y), sec(z) {};
        friend Time operator+(Time &t1, Time &t2);//重载“+”运算符函数作为友元函数
        void show();
    };
    //Time.cpp
    #include<iostream>
    using namespace std;
    #include"Time.h"
    void Time::show() {
        cout << hour << ":" << minute << ":" << sec;
    }
    //main.cpp
    #include"Time.h"
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    using namespace std;
    Time operator+(Time &t1, Time &t2) {
        Time mt;
        int jinwei = 0;
        mt.sec = t1.sec + t2.sec;
        if (mt.sec >= 60) {
            mt.sec -= 60;
            jinwei = 1;
        }
        mt.minute = t1.minute + t2.minute + jinwei;
        jinwei = 0;
        if (mt.minute >= 60) {
            mt.minute -= 60;
            jinwei = 1;
        }
        mt.hour = t1.hour + t2.hour + jinwei;
        return mt;
    }
    int main() {
        Time t1(10, 10, 10), t2(20,55,55),t3;
        t3 = t1 + t2;
        t1.show();
        cout << "+";
        t2.show();
        cout << "=";
        t3.show();
        cout << endl;
        getchar();
    }
    /*
    10:10:10+20:55:55=31:6:5
    */
    View Code

    由于已经将+运算符重载为Time类的友元函数,因此语句t3+t1+t2;编译称为调用普通函数的形式:t3=operator+(t1,t2);。

    二者区别?(精髓)

    运算符重载函数可以是类的成员函数,也可以是类的友元函数,那么二者有何区别,在参数个数和调用方式有什么不同?

    把Time类的定义及主函数修改如下:

    class Time{
        private:
        int hour,minute,sec;
        public:
        Time(){hour=0;minute=0;sec=0;}
        Time(int x,int y,int z){hour=x,minute=y;sec=z;}
        Time(int z){hour=0;minute=0;sec=z;}
        friend Time operator+(Time &t1,Time &t2);
    };
    int main(){
        Time t1(10,10,10),t2;
        t1=t1+45;
        t2=45+t1;
        return 0;
    }

    编译系统怎样处理语句t2=t1+45;呢?在编译时,系统发现运算符左侧的t1是Time类对象,右侧45是一个整数。那么编译系统首先寻找有没有对+运算符的重载,发现有operator+()函数,它是类的友元函数,要求两个Time类的形参,而现在45是一个整数,不符合要求。然后编译系统就去找有没有转换构造函数,发现有Time(int z)这个转换构造函数,于是去调用Time(int z)这个转换构造函数,将45转换为Time类常量后,才去调用operator+()函数。该过程相当于执行语句t2=t1+Time(45);同理,语句t2=45+t1;相当于执行语句t2=Time(45)+t1;

    如果主函数不变,修改Time类的定义如下:

    class Time{
        private:
        int hour,minute,sec;
        public:
        Time(){hour=0;minute=0;sec=0;}
        Time(int x,int y,int z){hour=x,minute=y;sec=z;}
        Time(int z){hour=0;minute=0;sec=z;}
        Time operator+(Time &t);
    };

    编译系统怎样处理语句t2=t1+45;呢?在编译时,编译系统首先寻找有没有对+运算符的重载,发现有operator+()函数,它是类的成员函数,要求一个Time类的形参,而现在45是一个整数,不符合要求。然后编译系统就去找有没有转换构造函数,发现有Time(int z)这个转换构造函数,于是去调用Time(int z)这个转换构造函数,将45转换为Time类常量后,才去调用t1.operator+()函数。相当于执行语句t2=t1.operator+(Time(45));而语句t2=45+t1;会编译成t2=45.operator+(t1),由于45不是Time类对象不能调用Time类的成员函数,所以编译出错

     成员函数重载的+运算符不支持交换律,从这个例子中可以看出在第一个参数需要隐式转换的情形下,使用友元函数重载运算符是正确的选择

    什么时候应该用成员函数,什么时候应该用友元函数重载?

    由于友元的使用会破坏类的封装,因此从原则上说,要尽量将运算符函数作为类的成员函数。但考虑到各方面的因素,一般将单目运算符重载为成员函数,将双目运算符重载为友元函数。另外,C++规定,有的运算符(=、()、[ ]、->)必须定义为类的成员函数,(流插入运算符<<和流提取运算符>>、类型转换运算符)则使用友元函数重载。若一个运算符的操作需要修改类对象的状态时(如=、*= 和 ++),应该用成员函数重载,如果运算符的左右操作数类型不同,左操作数可以是常数或其他类型的数时,希望有隐式转换,则必须用友元函数。

    3.5重载++和--运算符

    ++和--运算符属于单目运算符,但是它们有两种使用方式,即前置方式++x 和后置方式 x++,其意义也不相同。前置方式是先自加,返回的是修改后的对象本身,而后置方式返回的是自加前的对象,然后对象自加。C++对此做了约定:在自增(自减)运算符重载中,增加(减少)一个int型形参,就是后置自增(自减)运算符函数,否则就是前置方式。

    int在这里只是一个占位符,用来区分函数是前置还是后置,并没有实际意义。

    【对Time类进行自增运算,每次走一秒,假设当前秒数小于59】

    方式一:作为成员函数

    //Time.h
    class Time {
    private:
        int hour;
        int minute;
        int sec;
    public:
        Time():hour(0), minute(0), sec(0) {};
        Time(int x, int y , int z ) :hour(x), minute(y), sec(z) {};
        Time operator++() { sec++; return *this; }
        Time operator++(int x) { Time t; t = *this; sec++; return t; }
        void show() { cout << hour << ":" << minute << ":" << sec<<endl; };
    };
    //main.cpp
    #include"Time.h"
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    using namespace std;
    int main() {
        Time t1(10, 10, 10), t2,t3;
        t1.show();
        t2 = ++t1;
        t1.show();
        t2.show();
        t3 = t1++;
        t1.show();
        t3.show();
        getchar();
    }
    /*
    10:10:10
    10:10:11
    10:10:11
    10:10:12
    10:10:11
    */
    View Code

    方式二:作为友元函数

    //Time.h
    class Time {
    private:
        int hour;
        int minute;
        int sec;
    public:
        Time():hour(0), minute(0), sec(0) {};
        Time(int x, int y , int z ) :hour(x), minute(y), sec(z) {};
        friend Time operator++(Time &t) { t.sec++; return t; }
        friend Time operator++(Time &t,int x) { Time tt; tt = t; t.sec++; return tt; }
        void show() { cout << hour << ":" << minute << ":" << sec<<endl; };
    };
    //main.cpp
    #include"Time.h"
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    using namespace std;
    int main() {
        Time t1(10, 10, 10), t2,t3;
        t1.show();
        t2 = ++t1;
        t1.show();
        t2.show();
        t3 = t1++;
        t1.show();
        t3.show();
        getchar();
    }
    /*
    10:10:10
    10:10:11
    10:10:11
    10:10:12
    10:10:11
    */
    View Code

    3.6重载流插入运算符和流提取运算符

    在程序中,人们希望能用流插入运算符<<来输入用户自己声明的类的对象的信息,用流提取运算符>>来输入用户自己声明的类的对象的信息,这就需要重载流插入运算符<<和流提取运算符>>。

    将<<和>>重载为友元函数的形式如下:

    istream & operator>>(istream &,自定义类 &);

    ostream & operator<<(ostream &,自定义类& ) ;

    cin是istream类的对象,cout是ostream类的对象。当执行cout<<3时,该语句被编译为cout.operator<<(3);然后去调用ostream类的operator<<()函数,而且调用后的返回值仍然是cout。

    【在Time类上,重载流插入运算符<<和流提取运算符>>】

    //Time.h
    #include<iostream>
    using namespace std;
    class Time {
    private:
        int hour;
        int minute;
        int sec;
    public:
        friend ostream& operator<<(ostream &output,Time &t);
        friend istream& operator>>(istream &input,Time &t);
    };
    //Time.cpp
    #include<iostream>
    using namespace std;
    #include"Time.h"
    ostream& operator<<(ostream& output, Time &t) {
        output << t.hour << ":" << t.minute << ":" << t.sec;
        return output;
    }
    istream& operator>>(istream& input, Time &t) {
        cout << "input hour,minute and second of a time:";
        input >> t.hour >> t.minute >> t.sec;
        return input;
    }
    //main.cpp
    #include"Time.h"
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    using namespace std;
    int main() {
        Time t1, t2;
        cin >> t1 >> t2;
        cout << "t1=" << t1 << endl;
        cout << "t2=" << t2 << endl;
        getchar();
    }
    /*
    input hour,minute and second of a time:10 20 30
    input hour,minute and second of a time:4 5 6
    t1=10:20:30
    t2=4:5:6
    */
    View Code

    在operator>>()函数最后有语句return input;,就是说执行cin>>t1以后返回值是istream类的input,而input是cin对象的引用(别名),接下来再执行cin>>t2。

    C++规定运算符>>(<<)重载函数的第一个参数和函数的类型都必须是istream(ostream)类型的引用,这就是为了返回cin(cout)的当前值,以便连续输入(输出)。

    如果把函数定义为成员函数,那么语句cout<<t;就会被编译为cout.operator<<(t),系统认为要调用cout对象所属类ostream类的成员函数operator<<(),这样就出错了。

  • 相关阅读:
    Java实现第八届蓝桥杯9算数式
    Java实现第八届蓝桥杯9算数式
    java实现第七届蓝桥杯寒假作业
    java实现第七届蓝桥杯寒假作业
    java实现第六届蓝桥杯隔行变色
    java实现第六届蓝桥杯无穷分数
    mysql-5.7.19-winx64服务无法启动解决方案
    MySQL集群搭建详解
    Windows下多个Mysql实例配置主从
    在一台机子上,安装,运行两mysql数据库实例
  • 原文地址:https://www.cnblogs.com/exciting/p/8306694.html
Copyright © 2011-2022 走看看