Chapter 11使用类

本章内容包括:

  • 运算符重载
  • 友元函数
  • 重载<<运算符,以便用于输出
  • 状态成员
  • 使用rand()生成随机值
  • 类的自动转换和强制类型转换
  • 类转换函数

11.1 运算符重载

运算符重载时一种形式的C++多态。
为了实现运算符重载,需要使用被称为运算符函数的特殊函数形式。运算符函数的格式如下:
operator op(argument-list)
op必须是有效的C++运算符,不能虚构一个符号。

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

运算符重载operator+()之后,由于+是从左向右结合的。
t4 = t1 + t2 + t3 -> t4 = t1.operator+(t2 + t3) ->
t4 = t1.operator+(t2.operator+(t3))因此可以计算。

11.2.1 重载限制

以下是关于重载运算符的限制:

  1. 重载后的运算符必须至少有一个操作数是用户定义的类型,防止用户为标准类型重载运算符。例如不能用运算符-重载为计算两个double值的和。
  2. 使用运算符时不能违反运算符原来的句法规则。不能修改运算符的优先级
  3. 不能创建新的运算符。
  4. 不能重载下面的运算符。
  • sizeof
  • .:成员运算符
  • .*:成员指针运算符
  • :::作用域解析运算符
  • ?::条件运算符
  • typeid:一个RTTI运算符
  • const_cast:强制类型转换运算符
  • dynamic_cast:强制类型转换运算符
  • reinterpret_cast:强制类型转换运算符
  • static_cast:强制类型转换运算符
  1. 下表中的大多是运算符都可以通过成员函数或非成员函数进行重载,但下面的运算符只能通过成员函数进行重载。
  • =赋值运算符
  • ()函数调用运算符
  • []下标运算符
  • ->通过指针访问类成员的运算符

可重载的运算符

+ - * / % ^
& | ~= ! = <
> += -= *= /= %=
^= &= |= << >> >>=
<<= == != <= >= &&
|| ++ -- , ->* ->
() [] new delete new[] delete[]

11.3 友元

C++控制对类私有成员的访问,通常公有类方法是唯一的访问途径,但有时候这种限制太严格,以至于不适合特定的编程问题。C++提供可另一种形式的访问权限:友元。友元有三种:

  • 友元函数
  • 友元类
  • 友元成员函数
    友元函数给运算符重载提供了非成员函数访问类数据的方式。

11.3.1 创建友元

  1. 将其原型放在类声明中,在原型声明前加关键字friend:有两点含义
  • 友元函数在类中声明,但不是成员函数,因此不能使用成员运算符来调用;
  • 友元函数不是成员函数,但有相同的访问权限
  1. 编写函数定义

友员函数是拓展接口的组成部分。
如果要为类重载运算符,并将非类的项作为第一个操作数,则可以用友元函数来反转操作数的顺序。

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

一个很有用的类特性是可以对"<<"运算符进行重载,使之能够与cout一起来显示对象的内容。
重载该运算符使用的是输出类的友元函数,不是系统流类的友元函数。
需要注意的是该友元函数为了实现输出需要返回对象的引用,例:

friend std::ostream & operator<<(ostream & os, const class & c);

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

对于成员函数版本来说,一个操作数通过this指针隐式的传递,另一个操作数作为函数参数的形式显示的传递。对于友元版本来说,两个操作数都作为参数传递。
一些运算符只能作为成员函数重载,其他的视情况而定。

11.5 再谈重载:一个矢量类

11.5.1 使用状态成员

枚举变量RECT、POL被称为状态成员,其描述对象所处的状态。
多种表示方式和类
类非常适合于在一个对象中表示实体的不同方面。有两种表示方法,第一种方法,存储多种表示方式:具体分以下几步:

  1. 在一个对象中存储多种表示方式;
  2. 编写类函数,以便给一种表示方式赋值时,将自动给其他方式赋值。
    第二种方法存储一种表示方式,使用方法来提供其他表示方式。
    通过内部处理转换,类允许从本质上看待一个量。

11.5.2 为Vector重载算数运算符

如果方法通过计算得到一个新的类对象,则应考虑是否可以通过类构造函数完成这种工作,可以避免麻烦,同时确保新对象按照正确的方式创建。
运算符-有一元版本和二元版本,运算符/只有二元形式。

11.5.3 对实现的说明

将接口与实现分离式OOP的目标,允许对实现进行调整,而无需修改使用这个类的程序中的代码。
两种实现方式对应不同的情况。

11.5.4 使用Vector类来模拟随机漫步

rand()函数,返回一个从0到某个值的随机整数。
srand()函数允许覆盖默认的种子值,重新找一个随机数序列。
time(0)函数返回当前时间,通常为从某一个日期开始的秒数。

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

可以将类定义成与基本类型或者另一个类相关,使得从一种类型转换为另一种类型是有意义的。
类使用构造函数进行隐式转换,该构造函数作为转换函数。例:
Stonewt myCat; myCat = 19.6;
有两个参数的构造函数如果第二个参数有默认值,则可以作为构造函数。
如果不希望进行这种类型变换,可以用关键字explicit,例:
explicit Stonewt(double lbs);
在用explicit限定了构造函数之后,该构造函数只能用于显式强制类型转换。
下面的情况会用于隐式转换:

  • 将Stonewt对象初始化为double值时
  • 将double值赋给Stonewt对象时
  • 将double传递给接受Stonewt参数的函数时
  • 返回值被声明为Stonewt的函数试图返回double值时
  • 在上述任意一种情况下,使用可转换为double类型的内置类型时

当且仅当转换不存在二义性时,才会进行这种二步转换。

11.6.1 转换函数

将Stonewt对象转换为double值。
构造函数可以实现某种类型到类类型的转换;
相反的转换需要使用特殊的C++运算符函数——转换函数。operator typeName();
创建转换函数,需要注意以下几点:

  • 转换函数必须是类方法;
  • 转换函数不能指定返回类型;
  • 转换函数不能有参数。

typeName指出了要转换的类型,因此不需要指定返回类型。
自动应用类型转换
不存在二义性时,可以使用cout输出类,不需要定义。
用户不希望进行转换时,转换函数也可能进行转换。使用explicit可以取消这种自动类型转换。
除了使用explicit限定符,也可以用一个功能相同的非转换函数替换为该转换函数。
应谨慎的使用隐式转换函数,最好选择仅在被显示地调用时才会执行的函数。
C++为类提供下面的类型转换:

  • 只有一个参数的类构造函数用于将 类型与该参数相同的值 转换为 类 类型。在构造函数中声明explicit可以防止隐式转换,只允许显示转换。
  • 被称为转换函数的特殊类成员运算符函数,用于将类对象转换为其他类型。

11.6.2 转换函数和友元函数

实现加法时的选择
将double量和Stonerwt量相加,有两种选择:

  • 第一种方法将+函数定义为友元函数,构造函数将double隐式转换成Stonewt类型的参数。
  • 第二种方法时将运算符+重载为一个显式使用double类型参数的函数。

第一种方法,程序更简短,定义的函数较少;缺点是调用转换函数将增加时间和内存开销。
第二种方法,程序较长,运行速度较快。

11.7 复习题

1.使用成员函数为Stonewt类重载乘法运算符,该运算符将数据成员与double类型的值相乘,注意,用英石和磅表示,需要进位。也就是说,将10英石8磅乘以2等于21英石2磅。

Stonewt operator*(double n);
Stonewt Stonewt::operator*(double n)
{
	Stone multi;
	multi.pounds = pounds * n;
	multi.stone = int(multi.pounds) / Lbs_per_stn;
	multi.pds_left = int(multi.pounds) % Lbs_per_stn + multi.pounds - int(multi.pounds)
	return multi;
}

2. 友元函数和成员函数的区别是什么?
友元函数是类方法之外的函数,使用时不需要使用类作用域运算符,成员函数是类方法,使用时需要对象和类作用域运算符
成员函数是类定义的一部分,通过特定的对象来调用。成员函数可以隐式的访问调用对象的成员,而无需使用成员运算符。
友元函数不是类的组成部分,因此被称为直接函数调用。友元函数不能隐式的访问类的成员,而必须将成员运算符用于参数传递的对象。
3. 非成员函数必须是友元才能访问类成员吗?
非成员函数必须是友元才可以访问类的私有成员。
4. 使用友元函数为Stonewt类重载乘法运算符,该运算符将double值与Stonewt值相乘。

friend Stonewt operator*(double n, const Stonewt & st);
Stonewt operator*(double n, const Stonewt & st)
{
	return st * n;
}

5. 哪些运算符不能重载?

  • sizeof
  • .
  • .*
  • ::
  • ?:
  1. 在重载运算符=、()、[]和->时,有什么限制?
    必须作为成员函数重载。
  2. 为Vector类定义一个转换函数,将Vector类转换为一个double类型的值,后者表示矢量的长度。
operator double() const;
Vecotr::operator double() const
{
	return mag;
}
posted @ 2021-12-26 18:53  Fight!GO  阅读(58)  评论(0编辑  收藏  举报