重载运算与类型转换

基本概念:

运算符的重载是具有特殊名字的函数;他们的名字由关键字operator和其后要定义的运算符号共同组成。和其他函数一样,重载的匀速阿福也含有返回类型、参数列表以及函数体。重载运算符函数的参数数量和该运算符作用的运算对象数量一样多。,对于二元运算符来说,左侧运算对象传递给第一个参数,右侧运算对象传递给第二个参数。除了重载的函数调用运算符operat()之外,其他重载运算符不能含有默认实参。如果一个运算符函数是成员函数则它的第一个(左侧)运算对象绑定到隐式的this上指针上,因此,成员运算符函数的(显示)参数数量比运算符的运算对象总数少一个。对于一个重载的运算符函数来说,它或者是类的成员或者至少含有一个类类型的参数,这一约定意味着当运算符作用于内置类型的运算对象时,我们无法改变该运算符的含义。

重载运算符的运算对象数量、结合律、优先级、与对应的用于内置类型的运算符完全一致。

我们只能重载已有的运算符,而无权发明新的运算符号。大多数运算符(不是全部)都可以重载。

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

输入与输出运算符:

输出运算符应该尽量减少格式化操作,留给使用者更多可能性。

重载函数是定义在同一个类中的,如果在不同类中定义了同名函数,那么他们只是同名函数而不是重载函数。

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

算数与关系运算符:

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

赋值运算符:

我们可以重载赋值运算符,无论形参的类型是什么,赋值运算符都必须定义为成员函数,复合赋值运算符通常情况下也应该这样做,这两类运算符都应该返回左侧运算对象的引用。

下标运算符:

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

对于一个类包含下标运算符,它通常会定义两个版本:一个返回普通引用,另一个是类的常量成员并且返回常量引用。

递增与递减运算符:

定义递增和递减运算符的类应该同时定义前置和后置版本,这些运算符通常应该被定义成类的成员,为了与内置版本保持一致,前置运算符应该返回递增或者递减后对象的引用

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

同样,为了与内置版本的后置运算符保持一致,其应该返回对象的原值(递增或递减之前的值),返回的形式是一个值,而非引用,因为此对象的真实值已经发生变化。

成员访问运算符:

(->)箭头运算符必须是类的成员,解引用运算符通常也是类的成员,尽管并非必须如此。

对箭头运算符返回值的限定:和大多数运算符一样,我们能令operator*完成任何我们指定的操作,箭头运算符则不是这样,它永远不能丢掉成员访问这个最基本的含义。当我们重载箭头时,可以改变的是箭头从哪个对象中获取成员,而箭头箭头获取成员这一事实测永远不变。重载的箭头运算符必须返回类的指针或者自定义了箭头运算符的某个类的对象。

函数调用运算符:

如果类重载了函数调用运算符,则我们可以像使用函数一样使用该类的对象。因为这样的类同时也能存储状态,所以与普通函数相比他们更加灵活。函数调用运算符必须是成员函数。一个类可以定义多个不同版本的调用运算符,相互之间应该在参数数量或类型上有所区别。如果类定义了调用运算符,则该类的对象称作函数对象(function object)。因为也可以调用这种对象。

lambda是函数对象,当我们编写一个lambda后,编译器将该表达式翻译成一个未命名类的未命名对象。在lambda表达式产生的类中含有一个重载的函数调用运算符。

lambda在编译器方面:当一个lambda表达式通过引用捕获变量时,将由程序负责确保lambda执行时所引用的对象确实存在,因此编译器可以直接使用该引用而无需再lambda产生的类中将其存储为数据成员;相反,通过值捕获的变量被拷贝到lambda中,因此,这种lambda产生的类必须为每个值捕获的变量建立对应的数据成员,同时创建构造函数,令其使用捕获的变量的值来初始化数据成员。lambda表达式产生的类不含默认构造函数、赋值运算符、默认析构函数;他是否含有默认的拷贝/移动构造函数则通常要视捕获的数据成员类型而定。

C++语言有几种可调用的对象:函数、函数指针、lambda表达式、bind创建的对象以及重载了函数调用运算符的类。不同类型的可调用对象可能具有相同的调用对象。我们可以使用function标准库来存储相同调用形式的可调用对象。function有对应的一系列操作。我们不能直接将重载函数的名字存入function类型的对象中,会具有二义性,解决方法的途径有:存储函数指针而非函数的名字,使用lambda表达式。

重载。类型转换与运算符:

我们同样能定义对于类类型的类型转换,通过定义类型转换运算符可以做到这一点。转换构造函数和类型转换运算符共同定义了类类型转换(class-type conversions)这样的转换有时也被称作用户定义的类型转换。

如果定义了显示的类型转换运算符,编译器通常不会将一个显示的类型转换运算符用于隐式类型转换,该规定存在一个例外:即如果表达式被用作条件,则编译器会将现实的类型自动转换应用与它,即显示的类型转换将被隐式地执行。

当我们使用两个用户定义的类型转换时,如果转换函数之前或之后存在标准类型转换,则标准类型转换将决定最佳匹到底是哪个。

在调用重载函数时,如果需要额外的标准类型转换,则该转换的级别只有当所有可行函数都请求同一个用户定义的类型转换时才有用。如果所需的用户定义的类型转换不止一个,则该调用具有二义性。因为无法确定调用哪个类型转换。只有当仅存在一个类型转换时,我们才能进行下一步,根据标准类型转换的级别确定调用哪一个函数。

如果我们对同一个类既提供了转换目标是算数类型的类型转换,也提供了重载的运算符,则将会遇到重载运算符与内置运算符的二义性问题。因为在一条有该类对象和算数类型的表达式中,无法确定是将该类对象转换成算数类型函数还是将算数类型转换成该类类型的对象。

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lucky登

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值