C++ primer plus 第六版 第十一章重点内容总结

本文深入解析C++中运算符重载的原理与应用,探讨如何通过重载运算符实现用户自定义类型的操作。同时,介绍了友元函数的概念,包括友元函数的定义与使用,以及如何利用友元函数实现类之间的数据访问。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.运算符重载

C++允许将运算符重载扩展到用户定义的类型,重载运算符的格式是operator op (argument-list)。用户定义好一个方法用以重载运算符的内部机理,再将该方法的名字换成operator op (argiment-list)。如operator+(),其他的函数头,参数列表都不变,调用时也不变。。也可以在标识符中使用字母,数字,或者下划线之外的其他字符。

Time Time::operator+(const Time &t) const

{

}

 

Time total=coding.operator+(fixing)

Total=coding+fixing;

 

这两种方法都将调用operator+方法,注意在运算符表示法中,运算符左侧是调用对象,运算符右边是作为参数被传递的对象。即编译器将根据操作数的类型来确定如何做。如对类对象搜索是否有用户定义的用于类对象的运算符,对于基本类型则采用基本类型。也可以将两个以上的对象相加。

 

注意传递引用速度将更快,占用内存将更小。且不能返回局部变量和临时对象的引用,它们将消失,引用将指向不存在的数据。

 

重载限制:

重载运算符不必是成员函数,但必须至少有一个操作数是用户定义的类型。这将防止用户为标准类型重载运算符。因此不能将-号重载为两个double 值的和。

 

使用运算符时不能违反运算符原来的句法规则,例如不能将求模运算符重载成使用一个操作数。同样也不能修改运算符的优先级,重载后的运算符与原来的运算符具有相同的优先级别。

 

不能创建新的运算符。不能重载以下运算符:

Sizeof, 成员运算符(。),成员指针运算符。*,作用域运算符::,条件运算符?:,typeid,const_cast,dynamic_cat,reinterpret_cast,static_cast.书本P387页可以查询可重载的运算符(成员函数或者非成员函数都可)。但是有一些只能用成员函数进行重载:=,(),【】,-》。

 

2.友元

通常情况下共有类方法是访问类对象私有部分的唯一方式。但c++又提供了另外一种形式的访问权限,友元。有三种,友元函数,友元类,友元成员函数。通过让函数成为类的友元,可以赋予该函数与类的成员函数相同的访问权限。

 

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

Friend Time operater*(double m,const Time & t);

该原型意味着虽然operator*()函数实在类声明中声明的但它不是成员函数。虽然operator*()不是成员函数,但它与成员函数的访问权限相同。

 

实现函数定义的时候,因为它不是成员函数所以不要使用Time::限定符。另外不要再定义中使用friend关键字。

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

{

   Time result;

   Long totalminutes=t.hour*mult*60+t.minutes*mult;

   Result.hours=totalminutes/60;

   Result.minutes=totalminutes%60;

   Return result;

}

如果为类重载运算符,并将非类的项作为其第一个操作数,则可以用友元函数来反转操作数的顺序。即不再使用成员函数可以避免使用类对象调用的情况,即可以把所有参数显示声明在函数中,此时就需要非成员函数的友元函数来完成该任务,既可以使用所有类的数据成员,方法,又可以不使用对象调用。

 

常用的友元:重载<<运算符。<<是智能的,能够识别所有的基本类型,这是因为对于每种基本类型都定义了相应的operator<<()重载运算符。若使用Time类的cout版本,不要再标准接口iostream中声明,而在Time类中声明,必须用友元函数,否则若采用成员函数,那么必须trip.operator<<() trip<<cout,调用,但若采用友元函数:

void operator<<(ostream &os,const Time &t)

{

   os<<t.hours<<t.minutes;

}

cout<<trip;

operator<<()因为要使用Time类的私有成员,所以必须是Time类的友元函数,然而它只是把ostream当作一个整体对象使用,所以不必要是ostream的友元函数。

 

<<另一种重载版本:

如果想像cout一样连续使用,那么需要每次cout返回的都应该是ostream对象的引用。此时修改函数为:
ostream &operator<<(ostream &os,const Time&t )

{

   Os<<t.hours<<t,minutes<<endl;

   Return os;

}

此时函数的返回值是传递给该函数的对象,相当于cout<<trip; 将被转换成operator(cout,trip);这样就可以从左到右一步一步输出信息。

 

这个operator版本还可以将输出写到文件中。

#include<fstream>

ofstream fout;

fout.open(“savetime.txt”);

Time trip(12,40);

fout<<trip;

最后一条语句将被转化成operator<<(fout,trip);

关于成员函数还是非成员函数:

成员函数和非成员函数都可以实现运算符重载,一般来说非成员函数都是友元函数。这样才能访问类的私有数据。如:

Time operator+(const time & t) const;//成员函数版本

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

加法运算符需要两个操作数,对于成员函数版本来说,一个操作数通过this指针隐式传递,另一个操作数作为函数参数显示传递,对于友元版本,两个操作数都作为参数来传递。非成员版本的重载运算符函数所需的形参数目与运算符使用的操作数数目相同,而成员函数版本所需的参数数目少一个。(其中一个操作数是被隐式传递的调用对象)

 

这两个原型都与表达式T2+T3匹配,其中T2和T3都是Time类型对象,也就说将T1=T2+T3转化为下面两个中的一个。

T1=T2.operator+(T3);

T1=operator+(T2,T3);

 

二维矢量类的设计:有两种方式表示矢量,可以用大小(长度)和方向(角度)描述矢量,可以用分量x和y表示矢量。修改矢量的一种表达方式后对象将自动更新另一种表示。友元函数不再类作用域中,因此若使用类成员则必须使用限定名称。

 

如果方法通过计算得到一个新的类对象则应考虑使用类构造函数来完成这种工作。这样做不仅简便而且可以确保新的对象是按照正确的方式创建的。

 

运算符重载是通过函数实现的,所以只要运算符函数的特征标不同,使用的运算符数量与相应的内置c++运算符相同,就可以多次重载同一个运算符。

 

类的自动换转和强制类型转换:

C++语言不自动转换不兼容的类型,如int *p=10;虽然可能用整数来表示地址,但是整数和指针完全不同。此时可使用强制类型转换,int *p =(int *) 10;强制将地址设置为10,虽然不一定有意义。

 

在定义特定常量的时候,如果它们是整数,可以用枚举或者static静态变量。

Enum{size=10};static const int size=14;

 

只有接受一个参数的构造函数才能作为转换函数,

Stonewt(double lbs);

Stonewt mycat;

mycat=19.6;

 

在这个赋值过程中,程序将使用构造函数来创建一个临时的Stonewt对象,并将19.6作为初始值。随后采用逐成员赋值的方式将该临时对象的内容复制到mycat中。这一过程是隐式转换。当禁止隐式转换时,采用

explicit Stonewt(double lbs);

但仍然允许强制转换,如mycat=Stonewt(19.6);mycat=(Stonewt)19.6;

 

隐式转换发生的情况:
将Stonewt对象初始化为double时

将double值赋值给Stonewt对象时

将double值传递给接受Stonewt参数的函数时

返回值声明为Stonewt的函数试图返回double值时

 

如mycat=7300;

先将int转化为double,然后用Stonewt(double)构造函数。不过当且仅当转换不存在二义性时才会进行这种二步转换。假如这个类还定义了构造函数Stonewt(long),那么编译器将拒绝这些语句。Int可能被转化成long或者double。因此调用存在二义性。

 

转换函数:构造函数只用于某种类型到类类型的转换。要进行相反的转换必须使用特殊的c++运算符函数—转换函数。转换函数是用户定义的强制类型转换。创建转换函数,要转换为typename类型,需要使用这种形式的转换函数。Operator typename();

注意:
转换函数必须是类方法。

转换函数不能指定返回类型。

转换函数不能有参数。例如转换为double的类型的函数原型是:

Operator double();将函数原型添加到类声明中,double是要转换成的类型,因此不需要指定返回类型,转换函数是类方法所以需要使用类对象来调用,因此函数不要参数。

 

另一个方法是用一个功能相同的非转换函数替换该转换函数即可,但仅在被显式调用时,该函数才会执行。即可将

Stonewt::operator int(){return int (pounds+0.5);}转化为

Int Stonewt::Stone_to_int(){return int (pounds+0.5);}

 

C++为类提供了下面的类型转换:

只有一个参数的构造函数用于将类型与该参数相同的值转换为类类型。例如将int值赋值给Stonewt对象,接受int参数的Stonewt类构造函数将自动被调用。

被称为转换函数的特殊类成员运算符函数,用于将类对象转换为其他类型。

 

实现加法时的选择:

 

要将double和Stonewt对象相加有两种选择,第一种是将下面的函数定义为友元函数,让Stonewt(double)构造函数将double类型的参数转换为Stonewt类型的参数。

Operator+(const Stonewt &,const Stonewt &);

第二种方法是:将加法运算符重载为一个显示使用double类型参数的函数

Stonewt operator+(double x);

Friend Stonewt operator+(double x,Stonewt &s);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值