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

xiaoxiao2021-02-28  65

 

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++类的学习(五)——纯虚函数与模板类

 

 

 

 

转载请注明原文地址: https://www.6miu.com/read-2627445.html

最新回复(0)