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();
}
posted @ 2021-03-28 17:37  ding-ding-light  阅读(115)  评论(0编辑  收藏  举报