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<<(),这样就出错了。

  • 相关阅读:
    使用CustomValidate自定义验证控件
    C#中金额的大小写转换
    Andriod出错之Unable to build: the file dx.jar was not loaded from the SDK folder!
    VC 编写的打字练习
    机房工作笔记Ping只有单向通
    web服务协同学习笔记(1)
    Dll 学习3 将MDI子窗口封装在DLL中
    机房工作学习文件共享
    Andriod出错之Failed to find an AVD compatible with target 'Android 2.2'
    Andriod出错之wrapper was not properly loaded first
  • 原文地址:https://www.cnblogs.com/exciting/p/8306694.html
Copyright © 2011-2022 走看看