zoukankan      html  css  js  c++  java
  • C++ 面向对象编程4 运算符重载

    原理和机制

      C++的运算符默认只支持基本类型,但是C++提供了实现类类型使用运算符的语法也就是运算符的重载。运算符的重载本质上是通过函数来实现,将类类型的运算过程写成一个特殊的函数,当对应类型遇到该运算时自动调用该函数。实现运算符的重载既可以通过成员函数,也可以使用全局函数来实现,其主要功能是进行对象与对象之间的操作,其操作逻辑也需要符合基本类型的逻辑,不然容易产生误会。

    一般双目运算符的重载

    1.成员函数重载(加法为例)

        返回值类型 operator+(第二个对象){.....}  
    

      当在程序中碰到了 “第一个对象+第一个对象”,就会调用上面的函数,注意第一个对象的类型必须是所在运算符的重载函数的类型,第二个对象类型也必须与函数的参数类型相对应,不然不能够重载,接下来的各种运算符重载也是如此,不在一一赘述。
    实验程序:分数加法运算

    #include <iostream>
    
    using namespace std;
    
    class Fraction{
    public:
        explicit Fraction(int x = 0, int y = 1):x(x), y(y){}; //explicit 不允许隐式转换
        
        void print()
        {
            cout<<this->x<<"/"<<this->y<<endl;
        }
    
        Fraction operator+(const Fraction &fb)
        {
            return Fraction(this->x*fb.y+fb.x*this->y,this->y*fb.y); 
        }
    
    private:
        int x;/*分子*/
        int y;/*分母*/
    };
    
    int main()
    {
        Fraction fa(1, 2);
        Fraction fb(1, 3);
    
        Fraction fc = fa + fb;
        fc.print();
    
        return 0;
    }
    

    输出结果:5/6

    2.全局函数重载

        返回值类型 operator+(第一操作数,第二操作数){......}
    

      与成员函数重载不同全局函数重载需要传递两个参数,只需要与函数的参数类型向对应,当在程序中碰到了“第一个对象+第一个对象”,就会调用上面的函数。需要注意的是,如果类外函数无法发文类内的成员,可以使用以下方法解决:
      1).将成员改为公有;
      2).使用友元;
      3).为成员提供访问的接口。
    实验程序:

    #include <iostream>
    
    using namespace std;
    
    class Fraction{
    public:
        explicit Fraction(int x = 0, int y = 1):x(x), y(y){}; //explicit 不允许隐式转换
        
        void print()
        {
            cout<<this->x<<"/"<<this->y<<endl;
        }
    
    friend Fraction operator+(const Fraction &fa, const Fraction &fb);
    
    private:
        int x;
        int y;
    };
    
    Fraction operator+(const Fraction &fa, const Fraction &fb)
    {
        return Fraction(fa.x*fb.y+fb.x*fa.y/*分子*/,fa.y*fb.y/*分母*/);
    }
    
    int main()
    {
        Fraction fa(1, 2);
        Fraction fb(1, 3);
    
        Fraction fc = fa + fb;
        fc.print();
    
        return 0;
    }
    

    输出结果:5/6
      编译器对类类型运算的处理(加法为例):
        1).当编译器遇到fa+fb,先去Fraction类中寻找一个成员函数operator+(const Fraction &),如果有,就调用该函数来计算fa+fb;
        2).如果没有,就去全局函数中寻找operator+(const Fraction &,const Fraction &),如果有,就调用该函数来计算fa+fb;
        3).如果没有,编译器报错。

    3.类类型和基本类型之间的运算

    Fraction+整数 ------- 可以用成员/全局来重载
    整数+Fraction ------- 只能用全局函数来重载
    

    双目运算符的重载

    1.输入运算符的重载(cin<<)

    1).先去cin对象的类型istream中找一个成员函数:operator>>(a的引用类型)
    2).如果找不到,就去找一个全局函数:operator>>(istream &is,a的引用类型)
    3).如果再找不到就报错
    
    //istream是C++自定义的类,内部不能修改。所以开发者只能将cin>>重载为全局函数,
    //并且将该函数声明为a对应类的友元
    

    2.输出运算符的重载(cout<<)

    1).先去cout对象的类型ostream中找一个成员函数:operator<<(a的常引用类型)
    2).如果找不到,就去找一个全局函数:operator<<(ostream &os,a的常引用类型)
    3).如果再找不到就报错
    
    //ostream是C++自定义的类,内部不能修改。所以开发者只能将cout<<重载为全局函数
    //并且将该函数声明为a对应类的友元
    

    3.单目运算符的重载

      假设编译器对单目运算符(#)的处理

    #对象 ------ 去成员函数中找一个operator#()一个函数
                 如果没有,就去全局函数中找operator#(对象)
                 也没有就报错
    对象# ------ 去成员函数中找一个operator#(int)一个函数
                 如果没有,就去全局函数中找operator#(对象,int)
                 也没有就报错   
    

    实验程序

    #include <iostream>
    #include <cstring>
    
    using namespace std;
    
    class A{
    public:
        explicit A(int num1, int num2 , const char* str1)
        {
            this->num1 = num1;
            this->num2 = num2;
            if(str1 != NULL)
            {
                this->str1 = new char[strlen(str1) + 1];
                strcpy(this->str1, str1);
            }
        }
        ~A()
        {
            if(this->str1 != NULL)
                delete[] this->str1;
        }
        void operator=(A& a)
        {
            this->num1 = a.num1;
            this->num2 = a.num2;
            if(a.str1 != NULL)
            {
                if(this->str1 != NULL)
                {
                    delete[] this->str1;      
                }
                this->str1 = new char[strlen(a.str1) + 1];
                strcpy(this->str1, a.str1);          
            }
        }
        A& operator++()
        {
            this->num1++;
            this->num2++;
            return *this;
        }
        A operator++(int)
        {
            A tmp(this->num1, this->num2, "xxx");
            this->num1++;
            this->num2++;
            return tmp;
        }
    
    friend ostream&  operator<<(ostream& _cout, const A& a);
    friend istream&  operator>>(istream& _cin,  A& a);
    private:
        int num1;
        int num2;
        char* str1;
    };
    
    ostream& operator<<(ostream& _cout,const A& a)
    {
        return _cout<<a.str1<<":"<<"num1:"<<a.num1<<" num2:"<<a.num2<<endl;
    }
    
    istream& operator>>(istream& _cin,  A& a)
    {
       return _cin>>a.num1>>a.num2;
    }
    
    int main()
    {
        //创建两个对象进行初始化
        A a1(0, 0, "xxx");
        A a2(0, 0, "xxx");
        //输入运算符的重载
        cin>>a1;
        //赋值运算符的重载
        a2 = a1;
        //输出运算符的重载
        cout<<a1<<a2;
        //自加运算符重载
        cout<<a2++;
        cout<<++a1;
    }
    

    输出结果:

    特殊运算符的重载

    1.数组对象

      当对象遇到[]运算符时,在成员函数中找一个operator[](int ...)函数,如果没有就报错。

    2.强转和函数对象

    1)强转重载
      当编译器遇到 (类型)对象 语法时,就会去对象对应类中寻找一个 operator 类型() 的成员函数,找不到报错;
    2)函数对象
      当编译器遇到 对象(实参) 语法时,就会去对象对应类中寻找一个 operator()(参数) 的成员函数,找不到报错。

    #include <iostream>
    #include <cstring>
    
    using namespace std;
    
    class myarray{
    public:
        myarray(int size=10):size(size)
        {
            this->pdata = new int[size];
            memset(this->pdata, 0, size*sizeof(int));
        }
        ~myarray()
        {
            delete[] this->pdata;
        }
        //重写拷贝构造
        myarray(const myarray &arr)
        {
            this->size = arr.size;
            //申请空间
            this->pdata = new int[this->size];
            //将内容拷贝过来
            for(int i=0;i<arr.size;i++){
                this->pdata[i] = arr.pdata[i];
            }
        }
        myarray& operator=(const myarray& arr)
        {
            //防止自赋值
            if(this==&arr)
                return *this;
    
            //先释放旧空间
            delete[] this->pdata;
            this->size = arr.size;
            this->pdata = new int[this->size];
            for(int i=0; i<arr.size; i++)
            {
                this->pdata[i] = arr.pdata[i];
            }
            return *this;
        }
        //重载[]运算    
        int& operator[](int n)
        {
            if(n>=0 && (n < this->size))
                return this->pdata[n];
        }
        //重载(强转):求首元素的值    
        operator int()
        {
            return this->pdata[0];
        }
        //重载():函数对象,用来求数组的和
        int operator()(const myarray& arr)
        {
            int sum = 0;
            for(int i = 0; i<arr.size; i++)
                sum +=arr.pdata[i];
            return sum;
        }
    friend  ostream& operator<<(ostream& os, const myarray& arr);
            
    private:
        int *pdata;
        int size;
    };
    
    ostream& operator<<(ostream& os, const myarray& arr)
    {
        for(int i=0; i<arr.size-1; i++)
            os<<arr.pdata[i]<<",";
    
        return os<<arr.pdata[arr.size-1];
    }
    
    int main()
    {
        myarray arr1(15);
        myarray arr2;
    
        arr2 = arr1;
    
        cout<<arr1<<endl;
        arr1[5] = 99;
        cout<<arr1[5]<<endl;
        cout<<arr1<<endl;
        cout<<arr2<<endl;
    
        arr2[0] = 100;
        arr2[1] = 1;
        arr2[2] = 2;
        arr2[3] = 3;
        arr2[4] = 4;
        arr2[5] = 5;
        arr2[6] = 6;
    
        cout<<(int)arr2<<endl;
        cout<<arr2<<endl;
        cout<<arr1(arr2)<<endl;
    
        return 0;
    }
    

    3.指针对象

      当编译器遇到 对象-> 语法时,就会去对象对应类中寻找一个 operator->() 的成员函数,找不到报错;
      当编译器遇到 对象 语法时,就会去对象对应类中寻 operator() 的成员函数,找不到报错,使用指针对象通常是为了管理其他对象的指针。

    #include <iostream>
    
    using namespace std;
    
    class A{
    public:
        A(){cout<<"A()"<<endl;}
        ~A(){cout<<"~A()"<<endl;}
        void show()
        {
            cout<<"show"<<endl;
        }
    };
    
    class myauto_ptr{
    public:
        myauto_ptr(A *p=NULL):a_ptr(p)
        {
            cout<<"myauto_ptr()"<<endl;
        }
        ~myauto_ptr()
        {
            cout<<"~myauto_ptr()"<<endl;
            delete a_ptr;
        }
        A *operator->()
        {
            return this->a_ptr;//返回A类对象的地址,需要用->进行访问
        }
        A& operator*()
        {
            return *this->a_ptr; //返回A类对象本身,放回的值可以用.进行访问
        }
    private:
        //要管理的指针
        A *a_ptr;
    };
    
    int main()
    {
        A *a1 = new A;
        myauto_ptr p1(a1);
        p1->show();
        (*p1).show();
    }
    
  • 相关阅读:
    Linux下通过Generic Binaries安装MySQL 5.5
    struts 2 中AJAX的使用及Struts 2 JSON的使用
    关于Abstract interface的问题。
    对于“Win8对开发者的影响”的一些看法
    RealtimeModifier Bug Report | RealtimeModifier Bug 反馈
    何为Tomcat内存
    SSH开发过程中的中文问题汇总
    Spring Test 整合 JUnit 4 使用总结
    创建Shell脚本方便MySQL服务端启动
    学习笔记 winForm move功能 与 drag 功能
  • 原文地址:https://www.cnblogs.com/ding-ding-light/p/14556681.html
Copyright © 2011-2022 走看看