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


前言

C++ Primer第十四章学习笔记


重载的运算符与原有运算符的优先级、结合性保持一致。
部分运算符不支持重载(::、:?、'.')
部分运算符不推荐重载(逻辑运算会破坏短路、','、'&')。
重载运算符可以作为成员函数(仅允许该对象调用,如'+='),也可以作为非成员函数(存在一个对象即可,operator+(a,b))使用。通常具有对称性的操作应该定义为非成员的。
重载运算符的返回类型应该与内置的类似,当重载了算术运算符,应当同样重载符合赋值运算符,避免矛盾。

一、输入和输出运算符

不能将其作为成员函数进行重载,由于经常需要访问类的非共有对象,因此需要将其设为友元。

Type &operator>>(ostream &os,const Type &){//不会改变对象,因此为常量引用
	...//输出内容
	return os;
}
Type &operator<<(istream &is,Type &){//非常量引用
	...//输入内容
	return is;
}

输出时,不应该额外输出换行,该内容应当由使用者控制;输入时,需要考虑输入错误数据的情况,避免不正确的数据被赋值

二、算术和关系运算符

通常为非成员函数,且不改变状态,因此是const引用
如果定义了复合赋值运算符,可以用它来定义算术运算符。

Type operator+(const Type &lhs,const Type &rhs){
	Type temp = lhs;
	temp += rhs;//调用复合赋值运算符
	return temp;
}

相等运算和不等通常定义一个,另一个调用

bool operator==(const Type &lhs,const Type &rhs){
	return lhs.成员1==rhs.成员1&&lhs.成员2==rhs.成员2}
bool operator!=(const Type &lhs,const Type &rhs){
	return !(lhs==rhs);
}

其他关系运算需要注意,是否存在逻辑可靠的<定义。

三、赋值运算符

除了拷贝赋值和移动赋值,赋值本身作为成员函数可以被重载,同样应该返回左侧运算对象的引用,同时注意先释放空间再创建新空间,不过不用检查对自身的赋值,因为形参不是引用。
复合赋值运算符可以是非成员函数,但最好是。

四、下标运算符

	operator[]必须是成员函数

返回值为元素的引用,同时最好定义常量版本和非常量版本。

Type& operator[](std::size_t n);//非常量
const Type& operator[](std::size_t)const;//常量 

六、递增和递减运算符

	operator++ -- 建议是成员函数

返回值为元素的引用,需要判断是否溢出来抛出异常(越界)
在区分前置和后置时,需要添加一个形参,并且返回值是原值,通过一个临时对象保存,此时不用判断异常,因为会调用前置完成操作。

Type &operator++();
Type operator++(int);//name++时,会自动补上该参数,如果需要显式调用,需要name.operator++(0);

七、成员访问运算符

箭头运算符必须是成员函数,解引用通常是,但不一定是。可以用解引用定义箭头运算符

箭头的目的是访问成员,因此即使有重载版本,当使用指针->成员时,仍然会选择默认版本的->,并非重载。只有当对象不是指针时,才能使用该重载。意思是重载不能影响访问成员的功能。

class Type{
public:
	Type &operator->();//重载
	int fun();
};
Type a,*ptr_a = &a;
a->();//重载版本
a->fun();//执行fun函数,即执行访问成员的功能,非重载
ptr_a.operator->.fun();//首先调用重载版本,然后调用函数fun
ptr_a->fun();//同上

八、函数调用运算符

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

重载该运算符,由于是类的一部分,可以访问类的成员,相比于使用函数更加灵活方便,可以用于泛型算法。

1、重载函数调用

Type operator()(参数);

lambda表达式实际是函数对象,编译器会将其翻译为未命名类的未命名对象,类似于对象中重载的const函数调用。默认情况下,lambda 不能改变它捕获的变量。因此默认情况下,由 lambda 产生的类当中的函数调用运算符是一个 const 成员函数。如果 lambda 被声明为可变的,则调用运算符就不是 const 的。
通过值捕获的变量,使用重载方式时,需要为其定义构造函数。使用引用捕获则不需要定义。

int sz; cin >> sz;
auto wc = find_if(words.begin(),words.end(),[sz](const string &s) { return a.size() >= sz; });

class SizeComp {
public:
    SizeComp(size_t n): sz(n) { }
    bool operator() (const string &s) const
    	{ return s.size() >=sz; }
private:
    size_t sz;//两者等价
};

auto wc = find_if(words.begin(),words.end(),SizeComp(sz));

2、标准库中定义的函数对象

在头文件functional中,标准库定义了一组表示算术运算符、关系运算符和逻辑运算符的类,每个类分别定义了一个执行命名操作的调用运算符。同样使用了模板。

plus<int> intAdd;//该对象将int的加法重载为函数调用
int s = intAdd(10,20);//s = 10+20
greater<Type>;//大于
less<Type>;//小于
logical_and<Type>;//逻辑且

3、可调用对象与function

C++中有几种可调用的对象:函数,函数指针,lambda表达式,bind创建的对象以及重载了函数调用运算符的类。这些可调用的对象也有类型,然而两种不同类型的可调用对象却可能共享同一种调用形式,此处的形式指返回值类型、参数类型。如int (int,int)可以满足加减乘除等运算函数,因此它们的形式相同,但对象本身的类型是不同的,可能是函数、函数指针、lambda表达式等。
可以利用标准库function类型将这些函数进行封装,统一为一种对象类型。

function<Type> name;//Type是返回值类型、参数类型

int add(const int &a,const int &b){return a+b;}
function<int(int.int)> f1 = add;        //函数指针

class divide{
public:
	int operator()(const int &a,const int& b){return a/b;}
};
function<int(int,int)> f2 = divide();    //函数对象类的对象化

function<int(int,int)> f3 = [](int i,int j){        //lambda表达式
                                return i*j;
                            }

当使用函数名时,可能会因为函数重载导致二义性,例如int的add和string的add,可以通过函数指针区分,也可以直接通过lambda消除二义性。

九、重载、类型转换与运算符

operator type() const;        //        将类类型的值转换为其他类型。

可以将类的对象转换为其他类型,通过重载类型转换运算符实现,此方法无返回类型、无参数,并且为const函数。将需要转换的类型名重载,需要该类型可以作为返回类型,因此不能是数组或者函数,可以是指针。
只有确实需要的场景才使用该方式,不然可能产生意外结果。为了避免该情况,新标准引入了显式的类型转换运算符。

explicit operator type() const;        //显式类型转换
Smallint a;
a+3;//错误,需要将a转换为int
static_cast<int>(a) + 3;//正确,强转

即使设置了强转,也不影响转换在if等情况下转换为bool

1、避免有二义性的类型转换

两种情况容易产生二义性:
(1)两个类提供相同的类型转化

class A{
	A() = default;
	A(const B&);//接受B的构造函数
};
class B{
	operator A()const;//将B转A
};
A f(const A&);//函数需要A类型的形参
B b;
A a = f(b);//二义性错误,无法区分是将B强转为A还是通过构造函数获得A类型
A a1 = f(b.operator A());//强转
A a2 = f(A(b));//构造

(2)定义了多个转换规则,而被转换的类型可以通过其他类型转换

后面内容主要在说明二义性的一些场景,暂时没碰到该情况,留待补充


总结

重载可以针对绝大多数运算符,函数调用的情况比较特别,值得好好学习领会,其它方式比较常见。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值