zoukankan      html  css  js  c++  java
  • [知识点] 1.5.3 运算符重载

    总目录 > 1 语言基础 > 1.5 C++ 进阶 > 1.5.3  运算符重载

    前言

    运算符重载是个强大而有意思的功能,最初的接触是因为不清楚如何使用 sort 从大到小排序而了解到了运算符重载,然后是得知如何实现 struct 的 sort 排序,很长一段时间没有在别的情况下再使用,可以说仅仅是冰山一角了。

    子目录列表

    1、概念

    2、二元运算符重载

    3、一元运算符重载

    4、特殊运算符重载

    1.5.3  运算符重载

    1、概念

    在 1.1  C 语言基础 中,我们已经大致介绍了运算符的概念。运算符丰富而强大,每个运算符能够操作多种数据类型,比如简单的 “+” 号能够在 int, double, char, ... 等等数据类型中使用。其实在 C++ 中,运算符并不被看作一个简单的符号,它其实属于一种特殊的函数,称为运算符函数,和其他函数一样,运算符函数也可以被重载,称为运算符重载。但是,运算符重载的命名规则和参数确定却有不同,其一般形式为:

    数据类型 operator 重载运算符(重载参数表) {
        ...
    }

    其中,operator特别标示运算符函数的限定词

    比如上述的加法,其实在 C++ 中内置了形式如下的加法运算符的重载函数:

    int operator + (int, int);
    double operator + (double, double);
    char operator + (char, char); 

    有了运算符重载函数,运算符的适用范围就不仅局限于基本数据类型了,举个例子:

    class Student {
        int id;
        double grade;
    public:
        ...
    };
    
    Student A, B, C, D;
    
    cout << A.grade + B.grade + C.grade + D.grade;

    假设对于包含 id 和 grade 的 Student 类,我们希望在计算各名学生的成绩 grade 时,可以直接相加而不是每次都需要调用该成员,那么我们添加一个运算符重载函数,告知编译器:我们直接相加 Student 类的意义是直接相加他们的 grade 值,如下:

    Student operator + (Student a, Student b) {
        return a.grade + b.grade;
    }
    
    cout << A + B + C + D;

    这是一个二元运算符的例子,运算符重载可以分为二元、一元运算符及一些特殊运算符重载,下面将一一进行介绍。

    重载运算符也有许多限制。首先,并不是所有运算符都可以被重载,尽管大多数确实可以。下列运算符的重载功能受到了限制:

    ① 不能被重载

    .   .*   ::   ?:

    ② 只能被重载为类成员函数

    =   []   ()   ->

    运算符重载过程中,原有含义、优先级、结合顺序和所需要的参数个数均不会被改变,更不能创造新的运算符。

    2、二元运算符重载

    二元运算符就是需要两个操作数的运算符,又称为双目运算符,比如 +, -, *, /, % 等等。对于二元运算 a @ b(@ 表示任意可重载二元运算符),可能会被解析为:

    > a.operator@(b)

    > operator@(a, b)

    前者是被重载为类的费静态成员函数,要求第一个参数必须为一个对象;后者是被解析为类的友元或普通重载函数。

    ① 非静态成员函数重载运算符

    以这种方式重载二元运算符时,只能够有一个参数,因为它实际上是函数的第二个参数,第一个参数由 this 指针隐式传递。一般形式如下:

    class 类名 {
        ...
        数据类型1 operator 重载运算符 ([类名 *this,] 数据类型2 参数名) {
            ...
        }
    };

    数据类型 1 和 2 同样与类名是一致的。

    ② 友元 / 普通函数重载运算符

    以这种方式重载时,需要两个参数。一般形式如下:

    class 类名 {
        ...
        [friend] 数据类型1 operator 重载运算符 (数据类型2 参数名, 数据类型3 参数名) {
            ...
        }
    };

    同样地,数据类型 1, 2, 3 通常与类名一致。

    下面的代码囊括了上述两种重载方式:

    class Complex {
        double r, i;
    public:
        ... 
        Complex operator + (Complex b) {
            return Complex(r + b.r, i + b.i);
        }
        friend Complex operator - (Complex a, Complex b) {
            return Complex(a.r - b.r, a.i - b.i);
        }
    };

    一般情况下两种方式均可使用,但对于不要求返回左值且可以交换参数次序的运算符函数,最好使用第 ② 种,因为参数与运算符所需类型不匹配时,编译器会隐式转换,而对于第 ① 种,隐式传递的指针并不会被转换类型。

    3、一元运算符重载

    一元运算符,顾名思义,只需要一个运算参数,比如 ++, -- 等。对于一元运算 @a(前缀一元运算)或者 a@(后缀一元运算),可能被解析为:

    > a.operator@()

    > operator@(a)

    同上,两者分别为非静态成员函数重载友元 / 普通函数重载。对于二元运算符,其分别需要一个和两个参数,同理,对于一元运算符,分别需要零个和一个参数。下面的代码囊括了这两种重载方式:

    class Time {
        int h, m, s;
    public:
        ... 
        Time& operator ++ () {
            ...
            return *this;
        }    
        friend Time& operator -- (Time &t) {
            ...
            return t;
        }
    };

    我们注意到,不同于二元运算符的代码,这里的函数返回类型均加上了 ‘&’,表示返回对象的引用,如果删除,则不能实现连续的运算。

    上述对自增自减的重载均为前缀一元运算,因为这些运算符既可以作为前缀也可以作为后缀,而为了进行区分,需要重载后缀一元运算时,则在参数表中附加一个无用的形式参数,比如:

    Time& operator ++ (int) {
        h++;
        return *this;
    }    
    friend Time operator -- (Time& t, int) {
        t.h--;
        return t;
    }

    4、特殊运算符重载

    ① 数组下标运算符

    数组下标本质上是一种运算,用运算符 [ ] 表示,是一种二元运算符,同样允许被重载,通过重载可以检查数组的大小,并可在访问数组元素时检查下标值是否越界,这些都是编译器原生没有支持的。

    [ ] 可以同时出现在赋值符的左侧和右侧,所以重载时常返回引用;只能被重载为类的非静态成员函数

    举个例子:

    class Person {
        char name;
        double salary;
    public:
        ...
        double& operator [] (char *n) {
            return this -> salary;
        }
    };

    (好像有点问题,或者是没体现出啥)

    ② 赋值运算符

    对于赋值运算符 =,在 1.5.1  类与对象 中的构造函数部分进行了介绍,此处省略。

    ③ 类型转换运算符

    类型转换在 1.2  C 语言数据类型 中有所介绍,其实类的构造函数具有类型转换的功能。再来回顾一下构造函数:

    class Date {
        ...
    public:
        Date(int y, int m = 1, int d = 1) {
            ...
        }
    };
    
    Date d(2020, 10, 3);
    d = 2019; 

    执行 d = 2019 语句时将实现类型转换,int 类型的 2019 将被隐式转换为 Date(2019)。

    构造函数只能实现其他类型向类类型的转换,反之,则需要手动重载,暂略。

    ④ 函数调用运算符

    暂略。

    ⑤ I / O 运算符

    默认情况下,C++ 的输入输出运算符 << 和 >> 只支持基本数据类型,如果要实现对自定义数据类型的输入输出,则需要进行重载。

    以输出运算符 << 为例,一般形式如下:

    ostream& operator << (ostream& 输出流对象名, 类名 对象名) {
        ...
        输出流对象名 << ...;
        return 输出流对象名; 
    }

    ostream 是定义与 <iostream> 头文件的输出流类,是一个内置类。<< 是一个二元运算符,第一个参数是 ostream 类的对象的引用,第二个参数是要输出的对象,由于这个过程不会改变输出对象的值,所以通常将其设置为 const 类型的引用,而无需复制实参以提高效率。

    输出运算符通常被重载为类的友元函数而非成员函数,因为被重载为成员函数时第一个参数必须是通过 this 指针传递的当前对象,而不是 ostream 类对象的引用。

    输入运算符 >> 基本同理,略。

  • 相关阅读:
    雪花算法解决的问题
    ServiceStack6000次限制破解
    电商 详情页面 nginx 配置 优先请求静态页,若没有请求动态页,同时生成静态页
    docker 使用汇总
    .net 5 新特性 -- EFCoreDBFirst 介绍 和 .NET5 AOP 5个Filter
    模板方法设计模式:定义抽象类-模板:定义业务流程,执行步骤--》各业务类继承抽象类,实现各自不同 具体的执行步骤
    通过代理模式(包一层),实现对业务增加功能如日志,异常处理,缓存结果,到达不破坏原有的业务代码,扩展了功能
    设计模式
    mongodb 基本操作(增删改查),事务,数据失效机制
    log4net.Config
  • 原文地址:https://www.cnblogs.com/jinkun113/p/13728996.html
Copyright © 2011-2022 走看看