第十一章——使用类

运算符重载

 运算符重载是一种形式的C++多态。之前介绍过的函数重载(定义多个名称相同但特征标不同的函数)让程序员能够用同名的函数来完成相同的基本操作,即使这些操作被用于不同的数据类型。

运算符重载将重载的概念扩展到运算符上,允许赋予C++运算符多种含义。(比如将*运算符用于地址,将得到存储在这个地址中的值,但将它用于两个数字时,得到的是它们的乘积)。C++根据操作数的数目和类型来决定采用哪种操作

C++允许将运算符重载扩展到用户定义的类型。

要重载运算符,需使用被称为运算符函数的特殊函数形式,格式如下:

operator op(argument-list)

 例如,operator +()重载+运算符,operator *()重载*运算符。op必须是一个有效的C++运算符,不能虚构一个新的符号

计算时间:一个运算符重载示例

 如果在Priggs的账户上花费了2小时35分钟,下午又花费了2小时40分钟,则总共花了多少时间呢?这个与加法概念相似,但要相加的单位(小时与分钟的混合)与内置类型不匹配。

首先我们先使用常规方法,然后介绍如何将其转换为重载运算符

mytime0.h

#ifndef MYTIME0_H_
#define MYTIME0_H_

class Time
{
private:
	int hours;
	int minutes;
public:
	Time();
	Time(int h, int m = 0);
	void AddMin(int m);
	void AddHr(int h);
	void Reset(int h = 0, int m = 0);
	Time sum(const Time& t) const;
	void Show()const;
};
#endif

Time类提供了用于调整和重新设置时间、显示时间、将两个时间相加的方法

mytime0.cpp

#include<iostream>
#include"mytime0.h"
Time::Time()
{
	hours = minutes = 0;
}
Time::Time(int h, int m )
{
	hours = h;
	minutes = m;
}
void Time::AddMin(int m)
{
	minutes += m;
	hours += minutes / 60;
	minutes %= 60;
}
void Time::AddHr(int h)
{
	hours += h;
}
void Time::Reset(int h, int m )
{
	hours = h;
	minutes = m;
}
Time Time::sum(const Time& t)const
{
	Time sum;
	sum.minutes = minutes + t.minutes;
	sum.hours = hours + t.hours + sum.minutes / 60;
	sum.minutes %= 60;
	return sum;
}
void Time::Show()const
{
	std::cout << hours << " hours, " << minutes << " minutes";
}

不要返回指向局部变量或临时对象的引用。函数执行完毕后,局部变量和临时对象将消失,引用将指向不存在的数据

usetime0.cpp

#include<iostream>
#include"mytime0.h"
int main()
{
	using std::cout;
	using std::endl;
	Time planning;
	Time coding(2, 40);
	Time fixing(5, 55);
	Time total;

	cout << "planning time = ";
	planning.Show();
	cout << endl;

	cout << "coding time = ";
	coding.Show();
	cout << endl;

	cout << "fixing time = ";
	fixing.Show();
	cout << endl;

	total = coding.sum(fixing);
	cout << "coding.Sum(fixing) = ";
	total.Show();
	cout << endl;

	return 0;
}

 程序最终的输出是

 添加 加法运算符

将Time类转换为重载的加法运算符很容易,只要将sum()的名称改为operator +()即可。 

像sum()一样,operator +()也是由Time对象调用的,它将第二个Time对象作为参数,并返回一个Time对象,因此可以像调用sum()那样来调用operator +()方法

total = coding.operator +(fixing);

也可以使用运算符表示法:

total = coding + fixing

这两种表示法都将调用operator +()方法。在运算符表示法中,运算符左侧的对象(这里为coding)是调用对象,运算符右边的对象(这里是fixing)是作为参数被传递的对象

operator +()函数的名称使得可以使用函数表示法或者运算符表示法来调用它。编译器将根据操作数的类型来决定如何做:

int a,b,c;
Time A,B,C;
c = a + b;    //use int addition
C = A + B;    //use addtion as defined for Time objects

 重载限制

多数C++运算符都可以用这样的方式重载,重载的运算符不必是成员函数,但必须至少有一个操作数是用户定义的类型

1.重载后的运算符必须至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载运算符

2.使用运算符时不能违反运算符原来的句法规则(例如不能将%运算符重载成使用一个操作数),同样也不能修改运算符的优先级

3.不能创建新运算符

4.不能重载下面的运算符

  • sizeof:sizeof运算符
  • . :成员运算符
  • .* :成员指针运算符
  • :: :作用域解析运算符
  • ? : :条件运算符
  • typeid:一个RTTI运算符
  • const_cast:强制类型转换运算符
  • dynamic_cast:强制类型转换运算符
  • reinterpret_cast:强制类型转换运算符
  • static_cast:强制类型转换运算符

表中的大多数运算符都可以通过成员或非成员函数进行重载,但 下面的运算符只能通过成员函数进行重载

  • =,赋值运算符
  • (),函数调用运算符
  • [ ],下标运算符
  • ->,通过指针访问类成员的运算符

 

友元 

C++控制对类对象私有部分的访问。通常共有类方法提供唯一的访问途径,但是有时候这种限制太严格,C++提供了另外一种形式的访问 

 友元有3种

 友元函数;友元类;友元成员函数;

 通过让函数成为类的友元,可以赋予该函数与类的成员函数相同的访问权限,这里先介绍友元函数

创建友元

 创建友元函数的第一步是将其原型放在类声明中,并在原型声明前加上关键字friend:

friend Time operator*(double m, const Time & t);

该原型意味着:

  • 虽然operator*()函数是在类声明中声名的,但它不是成员函数,因此不能使用成员运算符来调用
  • 虽然operator*()函数不是成员函数,但它与成员函数的访问权限相同

第二步是编写函数定义。因为它不是成员函数,所以不要使用Time::限定符,另外不要在定义中使用关键字friend,定义如下:

Time operator*(double m ,const Time & t)
{
    Time result;
    long totalminutes = t.hours * m * 60 + t.minutes *m;
    result.hours = totalminutes / 60;
    result.minutes = totalminutes % 60;
    return result;
}

总之类的友元函数是非成员函数,其访问权限与成员函数相同。

 常用的友元:重载<<运算符

可对<<运算符进行重载,使之能与cout一起来显示对象的内容

1.<<的第一种重载版本

要是Time类知道使用cout,必须使用友元函数。因为下面这样的语句使用两个对象,其中第一个是ostream类对象(cout):

cout<<trip;

 如果使用一个Time成员函数来重载<<,Time对象将是第一个操作数,则必须这样使用<<:

trip<<cout;

看起来很奇怪

但通过使用友元函数,可以像下面这样重载运算符

void operator<<(ostream &os, const Time &t)
{
    os<<t.hours<<"hours,"<<t.minutes<<"minutes";
}

这样就可以使用下面看起来正常的语句

cout<<trip;

新的Time类声明使operator<<()函数称为Time类的一个友元函数,但该函数不是ostream类的友元

2.<<的第二种重载版本

前面介绍的实现存在一个问题,像下面这样的语句可以正常工作

cout<<trip;

但不允许像通常的cout与<<那样

cout<<"Trip time: "<<trip<<"Tuesday";    //can't do it

所以对operator<<()函数进行修改,让它返回ostream对象的引用即可:

ostream & operator<<(ostream & os, const Time &t)
{
    os<<t.hours<<"hours,"<<t.minutes<<"minutes";
    return os;
}

注意返回类型是ostream&,即该函数返回ostream对象的引用

一般来说,要重载<<运算符来显示c_name的对象,可使用一个友元函数,其定义如下 :

ostream & operator<<(ostream & os, const c_name &obj)
{
    os<<...;
    return os;
}

 重载运算符:作为成员函数还是非成员函数

对于很多运算符来说,可以选择使用成员函数或非成员函数来实现运算符重载。

例如重载+运算符需要两个操作数。对于成员函数版本来说,一个操作数通过this指针隐式地传递,另一个操作数作为函数参数显式地传递;对于友元函数版本来说,两个操作数都作为参数来传递

非成员函数重载”+“

Num operator+(const Num &m1,const Num &m2)
{
    Num temp;
    temp.n = m1.n + m2.n;
    return temp;
}

成员函数重载”+“

Num Num::operator +(const Num &t)
{
    Num temp;
    temp.n = n + t.n;
    return temp;
}

猜你喜欢

转载自blog.csdn.net/yangSHU21/article/details/131676869