zoukankan      html  css  js  c++  java
  • [技术]浅谈重载操作符

    前言

    重载操作符可以成为强有力的工具,但不可抛弃与客户的契约而滥用,那样只会让程序更难让人理解。

    ——《c++面向对象高效编程》

    背景

    XXX:诶,你快过来

    博主:蛤蛤蛤?怎么了?

    XXX:你教教我那个星号,不是,乘号怎么打啊

    博主:乘号怎么打是什么意思啊 喵喵喵

    XXX:就是可以让这两个矩阵乘起来啊

    博主:。。。你不会是在说重载吧

    XXX:对啊= =

    博主:等我给你写完this,就来写重载

    基本介绍&引入

    照例引度娘:

    操作符重载,计算机科学概念,就是把已经定义的、有一定功能的操作符进行重新定义,来完成更为细致具体的运算等功能。操作符重载可以将概括性的抽象操作符具体化,便于外部调用而无需知晓内部具体运算过程。

    先举个栗子例子:

    int a,b,c;
    a=2333;
    b=666;
    c=a*b;

    这段代码的意思十分明显,就是声明了两个变量a和b,a赋值为2333,b赋值为666,c赋值为a*b,即1553778。

    重要的是,int类型是语言精妙的定义实现过的内置数据类型,我们一写a*b,语言就会很好的计算出它正确的值(你要是爆了int当我没说),但是,假如我们把int换个类型呢?语言还能否正确的计算出我们想要得到的结果呢?

    我们打下这样的代码:

    struct matrix{
        int data[100][100];
    }a,b;
    int main(){
        matrix c;
        c=a*b;
    }

    然后试着编译一下,就会出现:

    显然,这样是不行的,那么,我们是否有办法像使用int一样使用我们自己定义的结构体matrix呢?

    基本操作

    我们继续分析上面的编译信息,显然,编译器正在寻找一个叫'operator*'的东西,这个东西是什么呢?

    显然就是我们今天要讲的主题——重载操作符。

    operator,关键字,语法:

    返回类型 operator 操作符 (参数列表){}

    比如说,上面我们想要实现的那个矩阵乘,就可以像这样子实现:

    struct matrix{
        int data[2][2];
        matrix operator*(const matrix &a){
            matrix tmp;
            for(int i=0;i<2;i++)
                for(int j=0;j<2;j++){
                    tmp.data[i][j]=0;
                    for(int k=0;k<2;k++)
                        tmp.data[i][j]+=data[i][k]*a.data[k][j];
                }
         return tmp;
      } };

    这样我们再写两个matrix相乘,就可以开心地通过编译啦。

    我们看到了乘号的重载,那么同样的,我们也可以重载加号,减号,赋值运算符。。。

    事实上,除了 .   .*   ::  ?:  sizeof  typeid  这几个运算符不能被重载,其他运算符都能被重载。

    那么,有了*,自然可以有*=,而*=的实现完全可以依靠*的实现

    matrix operator*=(const matrix &a){
        *this=*this*a;
        return *this;
    }

    其中,*this为调用该函数(重载操作符其实就是以操作符为函数名的函数)的对象,更多有关this指针的用法可以参考我的上一篇博文

    如何定义与使用

    我们刚才已经见到了一种定义方法——将重载函数声明为成员函数,那么,还有其他定义重载的方法吗?

    当然有。

    我们完全可以把运算符重载函数声明为非成员函数,而此时使用最多的是友元函数。

    举个栗子例子:

    struct point{
        double x,y;
        inline friend bool operator<(const point &a,const point &b){
            return a.x==b.x?a.y<b.y:a.x<b.x;
        }
    };
    inline point operator-(const point &a,const point &b){
            return (point){a.x-b.x,a.y-b.y};
    }

    显然,在这里,一个运算符重载函数被声明为了(非成员)友元函数,另一个则是非成员函数。

    进阶

    现在,我们已经可以进行简单的重载了,但是,还有一些需要注意的特殊操作符

    ++

    自加操作符

    我们知道,自加操作符有前置与后置两种用法,区别如下:

    int a,b,c,d;
    a=2333;
    b=2333;
    c=a++;//运行完后,a为2334,c为2333
    d=++b;//运行完后,b和d都为2334

    那么显然这两种操作是不一样的(令人窒息的操作)。那么如何进行这两种重载呢?

    前置:返回值类型& operator++()

    后置:返回值类型& operator++(int)

     注意,为了区分前置++与后置++的区别,需要在参数后增加一个"int"以示区分。含有"int"的重载方式为后置++,否则为前置++。前置--与后置--类似用法。

    举个栗子例子:

    struct Int{
        int data;
        Int& operator++();//前置
        Int& operator++(int);//后置
    };

    实现就可以根据自己需要进行了。

    流输入输出>>与<<

    我们知道,想输入输出一个int,是十分简单的事,比如:

    int a;
    cin>>a;
    a+=666;
    a*=2333;
    cout<<a;

    但是,假如我们写下这个呢?

    struct matrix{
        blabla...
    }a;
    int main(){
        cout<<a;
    }

    显然是不可以的,所以我们需要重载。

    这里,我们不是要重载matrix的什么,而是重载istream&ostream里的某些东西。

    我们知道,cin是输入流对象,cout是输出流对象,所以我们要重载的对象是cin的>>与cout的<<

    以matrix(矩阵)为例,我一般的写法是:

    struct matrix{
        int data[100][100];
    };
    istream& operator>>(istream &in,matrix &x){
        for(int i=0;i<100;i++)
            for(int j=0;j<100;j++)
                in>>x.data[i][j];
        return in;
    }
    ostream& operator<<(ostream &out,matrix &x){
        for(int i=0;i<100;i++){
            for(int j=0;j<100;j++)
                out<<x.data[i][j]<<' ';
            out<<endl;
        }
        return out;
    }

    这样就很健康啦

    WARNING

    有约定需要注意:

    流对象不允许复制,所以只能返回引用

    原理

    事实上,我们在写

    struct Int{
      blabla...
    }
    Int
    a,b,c; a=2333; b=666; c=a+b;

    实际上是调用了

    c=a.operator+(b);

    我们完全可以把它看做一个函数,不过是函数名有些特殊罢了。

    原则

    1. 并不是所有的操作符都能被重载。除了. ,.* ,:: ,? : ,sizeof,typeid这几个运算符不能被重载,其他运算符都能被重载
    2. 重载不能改变该运算符用于内置类型时的函义,程序员不能改变运算符+用于两个int型时的含义。
    3. 运算符函数的参数至少有一个必须是类的对象或者类的对象的引用。这种规定可以防止程序员运用运算符改变内置类型的函义。
    4. 重载不能改变运算符的优先级。
    5. 重载不能改变运算符的结合律。
    6. 重载不能改变运算符操作数的个数。比如+需要两个操作数,则重载的+也必须要有两个操作数。

    还有一个很重要的原则

    不可改变操作符固有的含义,要保留与客户之间约定的契约,如果你将+重载为减法或是乘法,这会使你的程序更加令人费解

    总结

    重载操作符是一个强大的工具,运用好重载,可以使你的代码更简洁,明白。

     

  • 相关阅读:
    对坐标点的离散化
    线段树-离散化处理点
    树状数组
    线段树
    dfs
    vector
    go 参数传递的是值还是引用 (转)
    go 数组指针 指针数组
    go 协程
    go 接口实现
  • 原文地址:https://www.cnblogs.com/hzoi-mafia/p/7290119.html
Copyright © 2011-2022 走看看