C++ primer 第14章 操作重载与类型转换

基本概念

如果一个运算符函数是成员函数,则它的第一个(左侧)运算对象绑定到隐式的this指针上,因此,成员运算符函数的(显式)参数数量比运算符的运算对象总数少一个。

对于一个运算符函数来说,它或者是类的成员,或者至少含有一个类类型的参数:

错误,不能为int重定义内置的运算符
int operator+(int,int);

我们可以重载大多数运算符,但不是全部。我们只能重载已有的运算符,而无权发明新的运算符号。
在这里插入图片描述
对于一个重载的运算符来说,其优先级和结合律与对应的内置运算符保持一致。

直接调用一个重载的运算符函数

一个非成员运算符函数的等价调用

data1+data2; //普通的表达式
operator+(data1,data2); // 等价的函数调用

调用成员运算符函数

data1+=data2; //基于“调用”的表达式
data1.operator+=(data2); // 对成员运算符函数的等价调用

某些运算符不应该被重载

通常情况下,不应该重载逗号、取地址、逻辑与、逻辑或等运算符。

使用与内置类型一致的含义

在这里插入图片描述

选择作为成员或者非成员

当我们定义重载的运算符时,必须首先决定是将其声明为类的成员函数还是声明为一个普通的非成员函数。在某些时候我们别无选择,因为有的运算符必须作为成员;另一些情况下,运算符作为普通函数比作为成员更好。
在这里插入图片描述

%通常定义为非成员
%=通常定义为类成员,因为它会改变对象的状态
++通常定义为类成员,因为它会改变对象的状态
->必须定义为类成员,否则编译会报错
<<通常定义为非成员
&&通常定义为非成员
==通常定义为非成员
()必须定义为类成员,否则编译会报错

输入和输出运算符

IO标准库分别使用>>和<<执行输入和输出操作。对于这两个运算符来说,IO库定义了用其读写内置类型的版本,而类则需要自定义适合其对象的新版本以支持IO操作。

重载输出运算符<<

通常情况下,输出运算符的第一个形参是一个非常量ostream对象的引用。之所以ostream是非常量是因为向流写入内容会改变其状态,而该形参是引用是因为我们无法直接复制一个ostream对象。

第二个形参一般来说是一个常量的引用,该常量是我们想要打印的类类型。第二个形参是引用的原因是我们希望避免复制实参,而之所以该形参可以是常量是因为(通常情况下)打印对象不会改变对象的内容。
为了与其他输出运算符保持一致,operator<<一般要返回它的ostream形参。

示例代码:

ostream& operator<<(ostream&os,const Sales_data &item){
   
	//输出内容
	os<<item.isbn()<<" "<<item.units_sold;
	//返回ostream
	return os;
}

输出运算符尽量减少格式化操作

用于内置类型的输出运算符不太考虑格式化操作,尤其不会打印换行符,用户希望类的输出运算符也像如此行事。如果运算符打印了换行符,则用户就无法在对象的同一行内接着打印一些描述性的文本了。相反,令输出运算符尽量减少格式化操作可以使用户有权控制输出的细节。

输入输出运算符必须是非成员函数

与iostream标准库兼容的输入输出运算符必须是普通的非成员函数,而不能是类的成员函数。否则,它们的左侧运算对象将是我们的类的一个对象:

Sales_data data;
data<<cout; //如果operator<<是Sales_data的成员

因此,如果我们希望为类自定义IO运算符,则必须将其定义为非成员函数。IO运算符通常需要读写类的非公有数据成员,所以IO运算符一般被声明为友元。

重载输入运算符>>

通常情况下,输入运算符的第一个形参是运算符将要读取的流的引用,第二个形参是将要读入到的(非常量)对象的引用。该运算符通常会返回某个给定流的引用。 第二个形参之所以必须是个非常量是因为输入运算符本身的目的就是将数据读入到这个对象中。

输入运算符必须处理输入可能失败的情况,而输出运算符不需要。

输入时的错误:

  • 当流含有错误类型的数据时读取操作可能失败。
  • 当读取操作到达文件末尾或者遇到输入流的其他错误时也会失败。

通常情况下,输入运算符只设置failbit。除此之外,设置eofbit表示文件耗尽,而设置badbit表示流被破坏。

算术和关系运算符

通常,我们把算术和关系运算符定义成非成员函数以允许对左侧或右侧的运算对象进行转换。因为这些运算符一般不需要改变运算对象的状态,所以形参都是常量的引用。

算术运算符通常会计算它的两个运算对象并得到一个新值,这个值有别于任意一个运算对象,常常位于一个局部变量之内,操作完成后返回该局部变量的副本作为其结果。如果类定义了算术运算符,则它一般也会定义一个对应的复合赋值运算符。此时最有效的方式是使用复合赋值来定义算术运算符:

Sales_data operator+(const Sales_data &lhs,const Sales_data &rhs){
   
	Sales_data sum = lhs;//把lhs的数据成员拷贝给sum
	sum+=rhs; //使用复合赋值运算符将rhs加到sum中
	return sum;
}

如果类同时定义了算术运算符和相关的复合赋值运算符,则通常情况下应该使用复合赋值来实现算术运算符。

相等运算符

通常情况下,c++中的类通过定义相等运算符来检验两个对象是否相等。也就是说,它们会比较对象的每一个数据成员,只有当所有对应的成员都相等时才认为两个对象相等。

关系运算符

定义了相等运算符的类也常常(但不总是)包括关系运算符。特别是,因为关联容器和一些算法要用到小于运算符,所以定义operator<会比较有用。

通常情况下,关系运算符应该:

  • 定义顺序关系,令其与关联容器中对关键字的要求一致。
  • 如果类同时也含有 == 运算符的话,则定义一种关系令其与 == 保持一致。特别是,如果两个对象是 != 的,那么一个对象应该 < 另外一个。

如果存在唯一一种逻辑可靠的 < 定义,则应该考虑为这个类定义 < 运算符。如果类同时还包含 == ,则当且仅当 < 的定义和 == 产生的结果一致时才定义 < 运算符。

赋值运算符

我们可以重载赋值运算符,不论形参的类型是什么,赋值运算符都必须定义为成员函数。

示例代码:
在这里插入图片描述

复合赋值运算符

复合赋值运算符不非得是类的成员,不过我们还是倾向于把包括复合赋值在内的所有赋值运算都定义在类的内部。为了与内置类型的复合赋值保持一致,类中的复合赋值运算符也要返回其左侧运算对象的引用。

示例代码:
在这里插入图片描述
赋值运算符必须定义成类的成员,复合赋值运算符通常情况下也应该这样做。这两类运算符都应该返回左侧运算对象的引用。

下标运算符

表示容器的类通常可以通过元素在容器中的位置访问元素,这些类一般会定义下标运算符operator[ ]。

下标运算符必须是成员函数。

为了与下标的原始定义兼容,下标运算符通常以所访问元素的引用作为返回值,这样做的好处是下标可以出现在赋值运算符的任意一端。进一步,我们最好同时定义下标运算符的常量版本和非常量版本,当作用于一个常量对象时,下标运算符返回常量引用以确保我们不会给返回的对象赋值

示例代码:
在这里插入图片描述

递增和递减运算符

定义递增和递减运算符的类应该同时定义前置版本和后置版本。这些运算符通常应该被定义成类的成员。

为了与内置版本保持一致,前置运算符应该返回递增或递减后对象的引用。
在这里插入图片描述
在这里插入图片描述

区分前置和后置运算符

前置和后置版本使用的是同一个符号,意味着其重载版本所用的名字将是相同的,并且运算对象的数量和类型也相同。为了解决这个问题,后置版本接受一个额外的(不被使用的)int类型的形参。当我们使用后置运算符时,编译器为这个形参提供一个值为0的实参。尽管从语法上来说,后置函数可以使用这个额外的形参,但是在实际过程中通常不会这么做。这个形参的唯一作用就是区分前置版本和后置版本的函数,而不是真的要在实现后置版本时参与运算。

为了与内置版本保持一致,后置运算符应该返回对象的原值(递增或递减之前的值),返回的形式是一个值而非引用。

后置版本示例代码:
在这里插入图片描述

显式地调用后置运算符

StrBlobPtr p(a1);
p.operator++(0);  //调用后置版本的operator++
p.operator++();   //调用前置版本的operator++

成员访问运算符

箭头运算符 -> 必须是类的成员。解引用运算符 * 通常也是类的成员。

重载的箭头运算符必须返回类的指针或者自定义了箭头运算符的某个类的对象。

函数调用运算符

如果类重载了函数调用运算符,则我们可以像使用函数一样使用该类的对象。因为这样的类同时也能存储状态,所以与普通函数相比它们更加灵活。

函数调用运算符必须是成员函数,一个类可以定义多个不同版本的调用运算符,相互之间应该在参数数量或类型上有所区别。

如果类定义了调用运算符,则该类的对象称作函数对象。因为可以调用这种对象,所以我们说这些对象的“行为像函数一样”。

class absInt{
   
	int operator()(int val)const<
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值