第十四章:重载运算与类型转换

1.运算符重载
'返回类型 operator运算符(参数列表){...}   //运算符重载格式'

a1 + a2;                  //普通的表达式
operator+(a,b);           //等价的函数调用(非成员函数)

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

注意点:

  • 只能重载已有的运算符,而无权发明新的运算符。
  • 优先级和结合律与对应的内置运算符保持一致。
  • 不能被重载的运算符(. | .* | ?: | ::)。
  • 后置版本的++运算符以operator++(int)做区别。
2.成员或非成员的选择

①必须为成员:赋值(=),下标([ ]),调用(( )),成员访问箭头(->
②通常为成员:复合赋值运算符(+=),递增(++)、递减()和解引用(
③通常非成员:算术(+),相等性(==),关系运算符(<
④必须非成员:输入(>>),输出(<<)运算符(类似java中的toString()*),流均为标准库类,所以必须为非成员

3.函数对象

如果累定义了调用运算符,则该类的对象称作函数对象(function object)。

返回值 operator()(参数列表){...}

标准库定义的函数对象:
头文件< functional >

算术关系逻辑
plus< Type >equal_to< Type>logical_and< Type >
minus< Type >not_equal_to< Type >logical_or< Type >
multiplies< Type >greater< Type >logical_not< Type >
divides< Type >greater_equal< Type >
modulus< Type >less< Type >
negate< Type >less_equal< Type >

使用样例:sort(b,e,less< string >()* )

4.可调用对象与function

c++可调用对象:
  ①函数
  ②函数指针
  ③lambda表达式(匿名函数,调用一次居多)
  ④bind创建的对象(解决谓词参数的限定问题)
  ⑤重载了函数调用运算符的类(函数对象)
为什么引入function?(头文件< functional >)
例: 实现简单桌面计算器,从而定义函数表,用于存储指向这些可调用对象的“指针”

// ordinary function
int add(int i, int j) { return i + j; }
// lambda, which generates an unnamed function-object class
auto mod = [](int i, int j) { return i % j; };
// function-object class
struct div {
	int operator()(int denominator, int divisor) {
		return denominator / divisor;
	}
};
//构建从运算符到函数指针的映射关系
map<string, int(*)(int,int)> binops;
//正确:add是一个指向正确类型函数的指针
binops.insert({"+",add});
//错误:mod不是函数指针
binops.insert({"%",mod});
//mod和divide都无法存入binops,因为它们都不是函数指针

function< T >为类模板,就是为了解决该问题,实现调用对象的适配统一

function<T> f;      	//T为调用对象适配类型如:int(int,int)表示返回类型为int,参数为两个int类型的调用对象,默认为空,等价于function<T> f(nullptr);
function<T> f(nullptr);
function<T> f(obj); 	//obj为适配的可调用对象 

所以上例中的问题可以通过以下形式解决:

map<string, function<int(int, int)>> binops = {
	{"+", add},                                // function pointer
	{"-", std::minus<int>()},                  // library function object
	{"/", div()},                              // user-defined function object
	{"*", [](int i, int j) { return i * j; }}, // unnamed lambda
	{"%", mod} };                              // named lambda object
binops["+"](10,5);                             // calls add(10,5);

当然我们不能直接将重载函数的名字存入function类型的对象中,会产生二义性问题,此时,可以通过函数指针为中间件来解决歧义(使指针指向所需的函数,确定对应的函数类型)。

5.类型转换运算符

类型转换运算符(conversion operator)是类的一种特殊成员函数,负责将一个类类型的值转换成其它类型。
类型转换运算符既①无返回类型,也②无形参,必须定义为③成员函数,一般为④const(不改变待转换对象的内容)。

operator int() const {return val;}          //将一个类的值转换为int,可以直接与int型变量进行运算
explicit operator int() const {return val;} //需要通过static_cast<int>(var)进行显示转换

就算声明为explicit,在以下情况下(作为条件),还是会进行隐式转换
  ①ifwhiledo语句的条件部分
  ②for语句头的条件表达式
  ③逻辑运算符(!||&&)中的对象
  ④条件运算符(?:)的条件表达式
这也是为什么while(cin>>value)可以作为判断表达式的原因,此时当输入value值后,cin被istream operator bool类型转换函数隐式地进行了转换。

6.避免有二义性的类型转换

**情况一:**类B有两种方式转换到A(即A(B&)构造函数,B内有类型转换函数到A)

struct B;
struct A {
	A() = default;
	A(const B&);                      // converts a B to an A
};
struct B {
	operator A() const;               // also converts a B to an A
};
A f(const A&);
B b;
A a = f(b); 						  // error ambiguous: f(B::operator A())
									  // or f(A::A(const B&))

情况二: 两个转换对象都是算法类型(如int,double),两种类型都无法精确匹配long型,所以会存在转换上的歧义。

struct A {
	A(int = 0);                     // usually a bad idea to have two
	A(double);                      // conversions from arithmetic types
	operator int() const;           // usually a bad idea to have two
	operator double() const;        // conversions to arithmetic types
};
void f2(long double);
A a;
f2(a);                             // error ambiguous: f(A::operator int())
                                   // or f(A::operator double())
long lg;
A a2(lg);                          // error ambiguous: A::A(int) or A::A(double)

情况三: 运算符调用的歧义(SmallInt可以转换成int从而使用内置+运算符,同时int也可以通过构造函数隐式转换成SmallInt从而调用SmallInt中的+重载版本)

class SmallInt {
	friend
	SmallInt operator+(const SmallInt&, const SmallInt&);
public:
	SmallInt(int = 0);                   // conversion from int
	operator int() const { return val; } // conversion to int
private:
	std::size_t val;
};
SmallInt s1, s2;
SmallInt s3 = s1 + s2;                  // uses overloaded operator+
int i = s3 + 0;                         // error: ambiguous

此时s3+0;不知道该调用那个+运算符函数,把0转换成SmallInt然后使用SmallInt的+,或者把s3转换成int,然后对两个int执行内置的加法运算。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值