《C++ Primer Plus》第11章 使用类

第11章 使用类

运算符重载

运算符重载将重载的概念扩展到运算符上,允许赋予C++运算符多种含义,也允许将运算符重载扩展到用户定义的类型。

运算符函数:operatorop (argument-list)。

使用重载运算符时,可以视为使用相应的运算符函数替换运算符。

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

调用operator+ ( )方法可以有两种方式,一种是运算符函数表示法,一种是运算符表示法。在运算符表示法中,运算符左侧的对象是调用对象,运算符右侧的对象是作为参数被传递的对象。

重载限制

  • 重载后的运算符必须至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载运算符
  • 使用运算符时不能违反运算符原来的句法规则,也不能修改运算符的优先级
  • 不能创建新运算符
  • 不能重载下面的运算符
sizeof sizeof运算符
. 成员运算符
.* 成员指针运算符
:: 作用域解析运算符
?: 条件运算符
typeid 一个RTTI运算符
const_cast 强制类型转换运算符
dynamic_cast 强制类型转换运算符
reinterpret_cast 强制类型转换运算符
static_cast 强制类型转换运算符
  • 下面的运算符只能通过成员函数进行重载
= 赋值运算符
() 函数调用运算符
[] 下标运算符
->

通过指针访问类成员的运算符

友元

友元有三种:

  • 友元函数
  • 友元类
  • 友元成员函数

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

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

友元函数在类声明中进行声明,但其并非成员函数,并能使用成员运算符来调用。

友元函数不是成员函数,因此编写其定义时不使用类作用域限定符”::“,且在定义时不使用关键字friend。

友元可视为类的扩展接口(接口指可操作类成员的函数),它并不违反OPP数据隐藏的原则,况且只有类声明可以决定哪一个函数是友元,也就是说类声明仍然控制了哪些函数可以访问私有数据。

整体使用类对象的函数不必是友元,但将其作为友元的一个好处是,它将作为正式类接口的组成部分(友元函数在类原型中进行声明),并且以后发现需要访问类对象的私有数据(而非整体访问类对象)时,只要修改函数定义即可,而不必修改类原型。

如果要为类重载运算符,并将非类的项作为其第一个操作数,则可使用友元函数来反转操作数的顺序。

通过使用友元函数,可以按照需要的顺序获取操作数,而不必将自定义的类对象作为第一个操作数。

重载“<<”运算符以显示c_name对象的方法:

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

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

运算符重载可通过成员函数或非成员函数实现,其中非成员函数包括友元与非友元两种,友元函数可以直接访问类的私有数据,而非成员函数不可直接访问类的私有数据,但可以整体使用类。

对于双目运算符的重载,有两个操作数参与运算,在成员函数版本中,一个操作数通过this指针隐式传递(此操作数隐含在调用运算符函数的对象中),另一个操作数作为函数参数显式传递;在友元函数版本中,两个操作数都作为参数显式传递。

在定义运算符函数时,只能选择一种版本,否则将导致二义性错误。

再谈重载:一个矢量类

类非常适合于在一个对象中表示实体的不同方面。首先在一个对象中存储多种表示方式,然后编写这样的类函数,以便给一种表示方法赋值时,将自动给其他表示方法赋值。通过在内部的处理转换,类运行从本质上看待一个量。

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

将接口与实现分离是OPP的目标之一,这样允许对实现进行调整,而无需修改使用这个类的程序中的代码。

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

 对于内置类型,将一个标准类型变量的值赋给另一种标准类型的值时,如果这两种类型兼容,则C++自动将这个值转换为接收变量的类型。

C++语言不自动转换不兼容的类型,但在无法自动转换时,可以使用强制类型转换。

可以将类定义成与基本类型或另一个类相关,使得从一种类型转换为另一种类型是有意义的。

接受一个参数的构造函数可将类型与该参数相同的值转换为类。

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

使用关键字explicit可以关闭构造函数隐式转换这种自动特征,但仍允许显式转换,即显式强制类型转换。

构造函数在未使用关键字explicit时,可以用于以下的隐式类型转换:

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

转换函数

构造函数只用于从某种类型到类类型的转换,要进行相反的转换,必须使用特殊的C++运算符函数——转换函数。

转换函数声明:operator typeName()。

转换函数必须是类方法。

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

转换函数不能有参数。

在转换函数的原型中,typeName指出了要转换成的类型,因此不需要指定返回类型。

转换函数是类方法,需要通过类对象来调用,从而告知要转换的值,因此转换函数不需要参数。

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

  • 只有一个参数的类构造函数用于将类型与该参数相同的值转换为类类型。
  • 被称为转换函数的特殊类成员运算符函数,用于将类对象转换为其他类型。

将重载的加法定义为友元(而非类方法)可以让程序更容易适应自动类型转换。原因在于,两个操作数都成为函数参数,因此与函数原型匹配。

实现内置类型与类类型的相加,有两种选择。第一种方法是将加法定义为友元函数,函数参数均为类类型,让构造函数将内置类型的操作数转换为类类型(仅定义一个友元函数)。第二种方法是,将加法运算符重载为一个显式使用内置类型参数的函数(定义两个函数,一个是成员函数,实现类类型操作数在前、内置类型操作数在后的加法,另一个是友元函数,函数第一个参数为内置类型,第二个参数为类类型,实现内置类型操作数在前,类类型操作数在后的加法)。第一种方法(依赖于隐式转换)使程序更加简短,因为定义的函数较少。缺点是每次需要转换时,都将调用转换构造函数。第二种方法(增加一个显式的匹配类型的函数)使程序较长,但运行速度较快。

 

posted @ 2020-04-30 16:36  溪嘉嘉  阅读(168)  评论(0编辑  收藏  举报