【fishing-pan:https://blog.csdn.net/u013921430转载请注明出处】

前言

前面的两篇博文中介绍了类的一些基本特性,今天讲一讲运算符重载和友元。

运算符重载

运算符重载是C++中一种形式的多态,运算符重载将重载的概念运用到运算符上,赋予运算符更多地含义。也许乍然一听,似乎我们对它并不熟悉,其实它一直为我们使用,例如 * 运算符,将其运用于地址,将得到地址中的值;将其用于两个数值之间,那么它表示乘号。这就是运算符的重载,与函数重载类似,根据操作数和类型来决定采用哪种操作。

在定义一个类时,往往需要重载一些运算符。以Time类为例,一个Time类的对象具有hours和mintue两个属性。如果早上吃饭花了0小时45分钟,中午吃饭花了1小时0分钟,下午吃饭花了1小时30分钟,求一天共花多少时间在吃饭上面。这里肯定不能直接用加号,因为相加的单位与C++内置类型不符,此时我们可以重载加号,让Time类的对象能够直接相加。

运算符重载的格式

运算符重载的格式如下;

类名 operator 运算符(形参){函数体}

以重载Time类的加号为例,其声明和实现分别如下;

声明

Time operator+(const Time &t) const; 

实现

Time Time::operator+(const Time &t) const
{Time sum;sum.mintues = mintues + t.mintues;sum.hours = hours + t.hours + sum.mintues / 60;sum.mintues = sum.mintues % 60;return sum;
}

当Time类的对象直接像普通对象一样使用加号时,其本质是对象调用类的成员函数,例如

     Time sumtime=time1+time2;Time sumtime=time1. Operator+(time2);

是等价的。在运算符调用中,运算符左侧的对象是调用运算符的对象,右侧的对象是作为参数被传递的对象。

运算符重载的限制

很多运算符都可以进行重载,但是也是有所限制的。具体限制如下;

1. 重载后的运算符必须至少有一个操作数是用户定义的类型,这是为了防止程序员为标准类型重载运算符,可以确保程序正确运行。

2. 不能修改运算符的优先级,不能违反运算符原本的运算规则。例如在重载加号时,不能提供两个形参(友元除外),例如下面的这种重载方式就是不被允许的;

Time operator+(const Time &t1,const Time &t2) const;

因为加法是一种双目运算符。在重载为类的成员函数后,加法运算的第一项应该是调用函数的一个对象。所以在运算符重载时,参数表中的参数数目应该是重载运算符的操作数减1。

3. 不能创造新的运算符,例如不能定义operator**()运算符;

4. 不能重载以下运算符;

.:成员运算符

 .*:成员指针运算符

 :: :作用域运算符

?::条件运算符

siezof:sizeof运算符。

5. 很多运算符可以通过成员或者非成员函数进行重载,但是以下四种只能通过成员函数进行重载;

=:赋值运算符;

( ):函数调用运算符;

[ ]:下表运算符;

_>:通过指针方位类成员的运算符。

6. 自增运算符(++)与自减运算符(--)

由于自增和自减运算符是单目运算符,在重载时应该是没有参数的,但是又有前置与后置之分,例如++i与i++。为了隽星区分,C++做了规定;

Time operator++()      //前置
Time operator++(int)   //后置

友元

前面提到,当对象使用重载后的运算符时,其本质是运算符调用类的成员函数。那么如果想要重载输出运算符的话,其声明如下;

Time operator<<(ostream &os) const;

那么使用应该是下面这样;

time1 << cout;

这样的使用方法就非常让人疑惑了。通过友元,我们可以使用下面的方式进行运算符重载。

ostream & operator<<(ostream &os, const Time &t)           //友元在类外定义的时候,不需要添加friend;
{os << "hours:" << t.hours << "  " << "mintues:" << t.mintues << "  ";return os;
}

那么什么是友元呢?

友元的格式

C++中友元有三种,分别是友元函数,友元类,友元成员函数,这里介绍的是友元函数。

创建友元函数的第一步是声明,友元函数的声明放在类的声明中,并且在前面加上friend关键字。例如;

friend ostream& operator<<(ostream &os, const Time &t);

虽然友元在类中声明,但是它并不是成员函数,所以不能使用成员运算符来调用它,但是它却跟成员运算符有相同的访问权限,即可以通过友元访问类的私有成员。

第二步是定义友元,友元可以直接在声明的时候进行定义,即内联的定义。也可以定义在类外,定义在类外时,不需要加类作用域运算符,也不需要有friend关键字。

ostream & operator<<(ostream &os, const Time &t)           //友元在类外定义的时候,不需要添加friend;
{os << "hours:" << t.hours << "  " << "mintues:" << t.mintues << "  ";return os;
}

友元函数的使用

与普通的运算符重载成员函数一样,友元也可以直接调用

例如;

cout<<time1<<time2;

其等价于;

operator<<(operator<<(cout,time1),time2);

之所以能够连续输出两个对象,就是因为重载后的输出运算符返回了一个ostream对象,这一点与标准输出运算符是一致的。也可以看出,一个双目运算符,如果在类内重载,那它的参数数目为1,如果利用友元重载,其参数数目为2。

代码

照例,在此给出类实现的所有代码,以供大家参考

mytime.h

//------mytime.h
#ifndef MYTIME_H
#define MYTIME_H
#include <iostream>using namespace std;class Time
{//----------私有成员,类中的成员默认是私有的
private:int hours;int mintues;//----------共有成员
public:Time();                                                       //默认构造函数Time(int h, int m = 0);                                       //显式构造函数Time(const Time &);                                           //拷贝构造函数~Time();                                                      //析构函数void AddMin(int m);void AddHour(int h);void reset(int h = 0, int m = 0);//------展示函数show()     void Time::show() const{cout << "hours:" << hours << "  " << "mintues:" << mintues << "  ";}Time operator+(const Time &t) const;                          //运算符重载Time operator-(const Time &t) const;Time operator*(double n) const;friend Time operator*(double n, const Time &t)                //友元;{return t*n;                                               //在这里又调用了重载运算符   operator*(double n) const;}                                                             //内联形式的定义;friend ostream & operator<<(ostream &os, const Time &t);     //一个双目运算符在重载时,如果是以友元的形式声明的,那么他有两个形参;如果是类的成员函数,那么他只有一个形参;};//-------时间重置,内联函数
inline void Time::reset(int h, int m)
{hours = h;mintues = m;
}#endif

mytime.cpp

//--mytime.cpp#include <iostream>
#include "mytime.h"using namespace std;//-------默认构造函数
Time::Time()
{hours = mintues = 0;cout << "调用默认构造函数" << endl;
}//------显式的构造函数
Time::Time(int h, int m) :hours(h), mintues(m)
{cout << "调用显式构造函数" << endl;
}//------拷贝构造函数
Time::Time(const Time &t)
{hours = t.hours;mintues = t.mintues;cout << "调用拷贝构造函数" << endl;
}//------析构函数
Time::~Time()
{cout << "调用了析构函数" << endl;
}//-------小时相加
void Time::AddHour(int h)
{hours += h;
}//------分钟相加
void Time::AddMin(int m)
{mintues += m;hours += mintues / 60;mintues %= 60;
}//------重载+号
Time Time::operator+(const Time &t) const
{Time sum;sum.mintues = mintues + t.mintues;sum.hours = hours + t.hours + sum.mintues / 60;sum.mintues = sum.mintues % 60;return sum;
}//------重载-号
Time Time::operator-(const Time &t) const
{Time diff;int time1 = hours * 60 + mintues;int time2 = t.hours * 60 + t.mintues;diff.hours = (time1 - time2) / 60;diff.mintues = (time1 - time2) % 60;return diff;
}//-------重载乘号
Time Time::operator*(double n) const
{Time result;long totalMintues = n*hours * 60 + n*mintues;result.hours = totalMintues / 60;result.mintues = totalMintues % 60;return result;
}//-------友元输出操作符
ostream & operator<<(ostream &os, const Time &t)           //友元在类外定义的时候,不需要添加friend;
{os << "hours:" << t.hours << "  " << "mintues:" << t.mintues << "  ";return os;
}

main.cpp

//-----------------------
//main.cpp
//不用先生
//------------------------
#include <iostream>
#include "mytime.h"using namespace std;int main()
{{Time eat_breakfast(0, 45);Time eat_lunch(1, 0);Time eat_dinner(1, 30);Time swiming(0, 45);                   //非const对象,既可以调用const成员函数,也可以调用非const成员。const Time study(8, 5);                //const对象只能调用const成员函数。// study_cut_swim;Time study_cut_swim = study - swiming;      //调用运算符重载后的Time类的减号;Time Eat_time_day = eat_breakfast + eat_dinner + eat_lunch;    //调用了重载以后的加法;cout << "学习比游泳多花" << study_cut_swim << endl;           //调用友元输出运算符<<cout << "每周吃饭所花费的时间为" << (7 * Eat_time_day) << endl;  //调用了友元乘法以及输出运算符;}system("pause");return 0;
}

运行结果

已完。。

参考书籍《C++  Primer 第五版》、《C++ Primer Plus 第六版》

相关博客

【C++】C++类的学习(一)——初识类

【C++】C++类的学习(二)——构造函数、析构函数、拷贝构造函数以及this指针

【C++】C++类的学习(四)——继承与虚函数

【C++】C++类的学习(五)——纯虚函数与模板类

【C++】C++类的学习(三)——运算符重载与友元函数相关推荐

  1. C++运算符重载(友元函数方式)

    我们知道,C++中的运算符重载有两种形式:①重载为类的成员函数(见C++运算符重载(成员函数方式)),②重载为类的友元函数. 当重载友元函数时,将没有隐含的参数this指针.这样,对双目运算符,友元函 ...

  2. C++学习笔记(10)运算符重载,友元函数,友元类

    c++允许我们为运算符定义专门的函数,这被称为运算符重载: 运算符可以简化字符串的操作,'+',以及使用关系运算符比较字符串,[ ]运算符访问向量中的元素: 例如: #include <iost ...

  3. C++实训 面向对象 - 运算符重载与友元函数

    第一关 #include <iostream> using namespace std;/********* Begin *********/class Complex {//复数类的声明 ...

  4. 从零开始学C++之运算符重载(三):完善String类([]、 +、 += 运算符重载)、和运算符重载...

    在前面文章中使用过几次String类的例子,现在多重载几个运算符,更加完善一下,并且重载流类运算符. []运算符重载 +运算符重载 +=运算符重载 <<运算符重载 >>运算符重 ...

  5. 【C++】C++类的学习(五)——纯虚函数与抽象类

    [fishing-pan:https://blog.csdn.net/u013921430转载请注明出处] 前言 在前面的博客中讲到了虚函数,今天讲述一种特别的虚函数--纯虚函数,以及与之相关的抽象类 ...

  6. C++学习笔记 - 阶段三:C++核心编程 - Chapter7:类和对象-C++运算符重载

    阶段三:C++核心编程 Chapter7:类和对象-C++运算符重载 运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型 7.1 加号运算符重载 作用:实现两个自定义数 ...

  7. 设计复数类Complex,实现运算符重载。

    设计复数类Complex,实现运算符重载. 要求: (1)重载运算符"+"和"*",使之分别能用于复数相加和相乘.(30分) (2)重载运算符"< ...

  8. C++学习日记——运算符重载

    1. 基本用法 对已有的运算符重新进行定义,赋予其新的意义,适应不同的数据类型. 运算符重载分为成员函数实现,全局函数实现. 运算符重载使用的函数名为"operator" + &q ...

  9. C++——运算符的重载---以成员函数方式重载---以友元函数方式重载

    一.运算符的重载 1.运算符的重载 允许把标准运算符(如+ - * /等运算符)应用于自定义数据类型的对象,可以提高程序的可读性,运算符的重载本质上还是函数重载.运算符仅仅是语法上的方便,它是另一种函 ...

最新文章

  1. one pragmatical sqlhelper
  2. 另一个小程序 返回的支付结果如何得到_手把手教你测微信小程序
  3. 解决文件上传时,服务器中中文文件名乱码问题
  4. redis安装,主从集群
  5. asp.net验证提示美化效果
  6. python 线性回归模型_如何在Python中建立和训练线性和逻辑回归ML模型
  7. Incorporating Lexical Priors into Topic Models(即交互式主题模型的应用)论文阅读
  8. 2、Qt Project之鼠标事件监控
  9. python的智能算法_基于pythonQT及智能算法的快速规则定制方法与流程
  10. Java Android客户端开发
  11. 什么是深度卷积神经网络,卷积神经网络怎么学
  12. 什么软件可以编辑PDF,PDF怎么拆分页面
  13. 无线学习之mac80211专题-1-自适应速率控制算法
  14. 【mcuclub】模数转换ADC0832
  15. Java实现根据Word模板填充表格数据(poi方式),以及doc和docx转PDF,最全最详细版本,解决外部引用jar在linux上报ClassNotFound的问题。
  16. 中国软件,从繁荣走向文明
  17. android 仿qq相册功能,Android第四十九期 - 仿QQ空间上传功能+本地数据库存储
  18. USACO Training Section 1.3 Calf Flac 解题报告AC代码
  19. asp毕业设计——基于asp+access的网上远程教育网设计与实现(毕业论文+程序源码)——网上远程教育网
  20. 来聊聊云计算能否彻底改变业务和软件架构

热门文章

  1. asp之ajax技术:responstext中文乱码
  2. JS 数据结构之旅 :通过JS实现栈、队列、二叉树、二分搜索树、AVL树、Trie树、并查集树、堆
  3. 你所不知道的模块调试技巧 - npm link #17
  4. 面试官系统精讲Java源码及大厂真题 - 36 从容不迫:重写锁的设计结构和细节
  5. 面试官系统精讲Java源码及大厂真题 - 05 ArrayList 源码解析和设计思路
  6. Ngnix安装的几种常用方式
  7. 行为设计模式 - 命令设计模式
  8. Docker快速搭建Bugzilla
  9. VMware安装Centos7详细过程
  10. Linux(服务器编程):25---epoll复用技术实现统一处理信号事件源