当前课程知识点:C++语言程序设计进阶 >  第八章 多态性 >  运算符重载 >  运算符重载为非成员函数

返回《C++语言程序设计进阶》慕课在线视频课程列表

运算符重载为非成员函数在线视频

运算符重载为非成员函数

运算符重载为非成员函数

有些运算符不能重载为成员函数,例如二元运算符的左操作数不是对象,或者是不能由我们重载运算符的对象

运算符重载为非成员函数的规则

运算符重载为非成员函数的规则

表达式oprd1 B oprd2

等同于operator B(oprd1,oprd2 )

表达式 B oprd

等同于operator B(oprd )

表达式 oprd B

等同于operator B(oprd,0 )

例8-3 重载Complex的加减法和“<<”运算符为非成员函数

• 将+、-(双目)重载为非成员函数,并将其声明为复数类的友元,两个操作数都是复数类的常引用。 • 将<<(双目)重载为非成员函数,并将其声明为复数类的友元,它的左操作数是std::ostream引用,右操作数为复数类的常引用,返回std::ostream引用,用以支持下面形式的输出:

cout << a << b;

该输出调用的是:

operator << (operator << (cout, a), b);

源代码:

    //8_3.cpp
    #include <iostream>
    using namespace std;

    class Complex {
    public:
        Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) { }  
        friend Complex operator+(const Complex &c1, const Complex &c2);
        friend Complex operator-(const Complex &c1, const Complex &c2);
        friend ostream & operator<<(ostream &out, const Complex &c);
    private:    
        double real;  //复数实部
        double imag;  //复数虚部
    };

    Complex operator+(const Complex &c1, const Complex &c2){
        return Complex(c1.real+c2.real, c1.imag+c2.imag); 
    }
    Complex operator-(const Complex &c1, const Complex &c2){
        return Complex(c1.real-c2.real, c1.imag-c2.imag); 
    }

    ostream & operator<<(ostream &out, const Complex &c){
        out << "(" << c.real << ", " << c.imag << ")";
        return out;
    }

    int main() {    
        Complex c1(5, 4), c2(2, 10), c3;    
        cout << "c1 = " << c1 << endl;
        cout << "c2 = " << c2 << endl;
        c3 = c1 - c2;   //使用重载运算符完成复数减法
        cout << "c3 = c1 - c2 = " << c3 << endl;
        c3 = c1 + c2;   //使用重载运算符完成复数加法
        cout << "c3 = c1 + c2 = " << c3 << endl;
        return 0;
    }



下一节:虚函数

返回《C++语言程序设计进阶》慕课在线视频列表

运算符重载为非成员函数课程教案、知识点、字幕

大家好

欢迎回来继续学习

C++语言程序设计

这一节我们来学习

如何将运算符重载为

类外的非成员函数

我们知道

当我们要将运算符

重载为类的成员函数的时候

它的左操作数

必须是这个类的对象

如果我们面临的运算

它的左操作数不是类的对象

比如说我们要实现

一个实数加复数

实数在加号左边

那么我们还能够通过

重载成员函数

解决这个问题吗

那就不行了

这个时候我们就需要将

这个运算符重载为

类外的一个普通的全局函数

还有一种情况

虽然说一个运算符的左操作数

是类的对象

但是这个类

不是由我们自己定义的

比如说

是类库里面现成的类的对象

那么这个类也不由我们自己设计

我们也没有权利去给这个类

增加一个什么重载运算符函数

那么这个时候

如果我们要想能够让这个对象

跟我们自定义的

另外一个类的对象

去运算的话

我们就只能将运算符重载为

类外的非成员函数

接下来我们看看

具体的语法是什么样子

运算符要重载为

非成员函数的时候呢

它的形参就要列出

所有的操作数

比如说二元运算符

它的形参

就应该是两个操作数

两个形参

那么次序呢

是自左至右排列的各个操作数

那么重载为非成员函数的时候

后置的++和--

除了要一个

它是一个单步运算符

除了它要有一个形参

作为参与运算的这个对象以外

还要增加一个整数

以区分前置和后置的单步运算符

那么还有一点非常重要

就是当我们将运算符

重载为非成员函数的时候

参数中要至少有一个

是自定义类型的对象

不能两个参数

都是基本数据类型的

我们不能说

重载一个函数

然后把整数的加法含义

给修改一下

那这是绝对不可以的

所以只能是为自定义类型

去扩展

去重新定义这个已有的运算符

而不能对原有的数据类型

把它的运算的含义去修改了

这是不允许的

那么在运算符重载函数中呢

可能会需要

操作某个类对象的私有成员

这个时候通常的做法呢

就是将函数

声明为这个类的友元

当然

你不声明为友元也是可以的

我们可以通过类的公有接口

去访问私有数据成员

但是呢

去声明为友元以后呢

运算的效率就会比较高一些

如果我们希望

将一个双目运算符B

重载为类外的非成员函数

那么这样重载完以后

如果我们在程序中

写了这样的表达式

操作数1 B 操作数2

那么这个时候就相当于

调用了这样一个全局函数

oprdB

括号里的两个形参呢

分别是操作数1和操作数2

如果是重载前置的单目运算符B

那么重载完了以后

我们去用这个单目运算符

对一个对象oprd

进行操作的时候

就相当于调用oprdB这个函数

然后括号里有一个实参

就是参与运算的这个对象

而后置的单目运算符++--呢

它的参数

要比前置的多一个整数

调用的时候呢

第二个参数默认的是给它一个0

而且这个0

在函数体中

是不许去使用的

下面这个例子呢

就以C成员函数的形式

为复数类重载加法 减法运算

并且还为它重载了一个

插入运算符

这样的话

我们就可以用cout

整个输出一个复数对象了

重载插入运算符

就是只能重载为类外的成员函数

为什么呢

因为它的左操作数

虽然是一个类的对象

但是它是系统里面预定义好的

这个类库里面的一个

输出流类的对象cout

不是我们程序员自定义的对象

那这个时候

我们就无法在类库里面

这个输出流类里面

去加一个重载运算符函数

这个没有权限去加的

所以我们就把它重载为

类外的非成员函数

实现对于复数对象的

整体输出的功能

那么这个题目的具体要求呢

就是将+ -

这两个双目运算符

重载为非成员函数

并且呢

把它声明为复数类的友元

这样的话

它的运算效率会比较高一些

这样它的两个操作数呢

都是复数类的常引用

当然我们也可以

将复数类对象作为参数

但是传引用

比传对象的效率要高一些

但是传引用呢

就有可能实现双向传递了

那么在运算符进行运算的时候

如果出错

改变了操作数的值

那就不是我们希望的结果了

所以这里呢传引用

但是加一个const来修饰

传常引用

既提高了参数传递的效率

又保证了实参的安全性

然后将双目运算符

这个插入运算符

也重载为非成员函数

也将它实现为复数类的友元

它的左操作数呢

是这个类库里头

标准库里面这个ostream

这个类的对象引用

我们在验证它的时候呢

会传给它cout

这个预定义好的对象

至于ostream这个类是个什么

我们在十一章会给大家介绍

io流类库的时候

大家就清楚了

那么右操作数呢

是复数类对象的常引用

这个操作符的函数

它要求返回

必须是一个ostream的引用

那在这个重载了以后呢

我们要求它能够支持

这种级联的输出

cout它用插入运算符

输出一个对象a

再用插入运算符输出一个对象b

那么实际上它是

两次调用了这个operator

插入运算符函数

第一次调用 先输出a

输出完了a以后

这个函数本身的返回值

就是一个ostream的对象引用

所以又把它作为

第二次调用的第一个参数

这样就实现了

这样的级联的输出

接下来呢

我们来看一下

这个例题的源代码

以及它的运行效果

这一次呢

我们将这个加法和减法

还有插入运算符函数

都重载为类外的全局函数

我们看一下

这几个函数的实现

它不再是

Complex类的成员函数了

它就是普通类外的独立的函数

所以说呢

要做加法

那么两个操作数都得作为参数

传过来

减法也是一样

操作数是两个

那么插入运算符呢

它的第一个操作数

是ostream的对象

是一个输出流类的对象

因为你要指定

往哪个输出流里面插入

对吧

到目前为止呢

我们所用过的ostream对象呢

就是这个cout

向显示器输出的这个输出流

第二个参数呢

就是我们要输出的这个复数对象

由于我们需要在函数体中

去访问复数对象的数据成员

为了提高访问效率呢

我们希望能够直接通过

对象名.成员名的方式访问

所以在这个复数类中

就把这三个运算符重载函数

都声明为友元了

那么这个加法减法函数的实现

跟前面的成员函数的实现

都是类似的

就是两个操作数的实部

虚部分别相加

两个操作数的实部

虚部分别相减

然后构造临时无名对象 返回

那么现在看

这个插入运算符函数

刚才解释的这两个参数

还有呢

这个函数的返回值

要求必须是一个ostream

对象的引用

通常就是应该将第一个操作数

返回

所以我们就将这第一个流对象

作为返回值返回

返回以后

它就结果还是一个输出流对象

所以呢

它能够去实现级联的输出

现在我们看

在主函数中验证一下

仍然是定义两个对象c1 c2

然后我们在这两个对象上面

作减法 作加法

这个效果跟前面

我们把它定义成成员函数

效果是一样的

那接下来呢

我们再用cout

用插入运算符去输出对象c3

这一次呢

我们不用写什么display函数

或者显示其他的显示函数了

对象可以整体输出了

那么这个左操作数是cout

实际上这是一个级联输出

级联输出了一个字符串

然后这第一次输出

输出字符串以后

它的返回值仍然是cout

所以在输出c3的时候

这个插入运算符

它的左侧的操作数

还是cout对象

然后把c3输出

输出完了以后

我们自己定义的

这个插入运算符函数

也是遵循的这种规则

它返回的也是第一个参数

所以它返回的还是cout对象

那么再用cout去输出这个换行

C++语言程序设计进阶课程列表:

第七章 继承与派生

-导学

--导学

-继承的基本概念和语法

--继承的基本概念和语法

-第七章 继承与派生--继承的基本概念和语法习题

-继承方式

--继承方式简介及公有继承

--私有继承和保护继承

-第七章 继承与派生--继承方式

-基类与派生类类型转换

--基类与派生类类型转换

-第七章 继承与派生--基类与派生类类型转换

-派生类的构造和析构

--派生类的构造函数

--派生类的构造函数举例

--派生类的复制构造函数

--派生类的析构函数

--第七章 继承与派生--派生类的构造和析构

-派生类成员的标识与访问

--访问从基类继承的成员

--虚基类

-第七章 继承与派生--派生类成员的标识与访问

-小结

--小结

-综合实例

--第七章综合实例

-实验七

--实验七

-第七章讲义

第八章 多态性

-导学

--导学

-第八章 多态性--导学

-运算符重载

--运算符重载的规则

--双目运算符重载为成员函数

--单目运算符重载为成员函数

--运算符重载为非成员函数

-第八章 多态性--运算符重载

-虚函数

--虚函数

--虚析构函数

--虚表与动态绑定

-第八章 多态性--虚函数

-抽象类

--抽象类

--第八章 多态性--抽象类

-override与final

--override与final

-第八章 多态性--override与final

-小结

--第八章小结

-综合实例

--第八章综合实例

-实验八

--实验八

- 第八章讲义

第九章 模板与群体数据

-导学

--导学

-模板

--函数模板

--类模板

-第九章 模板与群体数据--模板

-线性群体

--线性群体的概念

-第九章 模板与群体数据--线性群体

-数组

--数组类模板

--例9-4数组类应用举例

-链表

--链表的概念与结点类模板

--链表类模板

-第九章 模板与群体数据--链表

-栈

--栈类模板

--栈类模板课后习题

--例9-9 栈的应用

--例9-9 栈的应用课后习题

-队列

--队列类模板

-第九章 模板与群体数据--队列

-排序

--排序概述

--插入排序

--选择排序

--交换排序

-第九章 模板与群体数据--排序

-查找

--查找

--查找课后习题

-小结

--小结

-综合实例

--综合实例

-实验九

--实验九

- 第九章讲义

第十章 泛型程序设计与C++标准模板库

-导学

--导学

-泛型程序设计及STL的结构

--泛型程序设计的基本概念

--STL简介

-第十章 泛型程序设计与C++标准模板库--泛型程序设计及STL的结构

-迭代器

--迭代器

-第十章 泛型程序设计与C++标准模板库--迭代器

-容器的基本功能与分类

--容器的基本功能与分类

-第十章 泛型程序设计与C++标准模板库--容器的基本功能与分类

-顺序容器

--顺序容器的基本功能

--顺序容器的特征

--顺序容器的插入迭代器与适配器

--第十章 泛型程序设计与C++标准模板库--顺序容器

-关联容器

--关联容器分类和基本功能

--集合

--映射

--多重集合和多重映射

-第十章 泛型程序设计与C++标准模板库--关联容器

-函数对象

--函数对象

--函数适配器

-算法

--算法

-小结

--第十章小结

-综合实例

--综合实例

-实验十

--实验十

- 第十章讲义

第十一章 流类库与输入/输出

-导学

--导学

-I/O流的概念及流类库结构

--I/O流的概念及流类库结构

-第十一章 流类库与输入/输出--I/O流的概念及流类库结构

-输出流

--输出流概述

--向文本文件输出

--向二进制文件输出

--向字符串输出

-第十一章 流类库与输入/输出--输出流

-输入流

--输入流概述

--输入流应用举例

--从字符串输入

-第十一章 流类库与输入/输出--输入流

-输入/输出流

--输入/输出流

-第十一章 流类库与输入/输出--输入/输出流

-小结

--小结

-综合实例

--综合实例

-实验十一

--实验十一

- 第十一章讲义

第十二章 异常处理

-导学

--第12章导学

-异常处理的思想与程序实现

--异常处理的思想与程序实现

-第十二章 异常处理--异常处理的思想与程序实现

-异常处理中的构造与析构

--异常处理中的构造与析构

-第十二章 异常处理--异常处理中的构造与析构

-标准程序库异常处理

--标准程序库异常处理

-第十二章 异常处理--标准程序库异常处理

-小结

--第12章小结

-综合实例

--综合实例

-实验十二

--实验十二

- 第十二章讲义

运算符重载为非成员函数笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。