概述
C++允许我们重定义操作符用于类类型对象时的含义,如果需要,可以像内置转换那样使用类类型转换,将一个类型的对象隐式转换到另一个类型。
1 重载操作符的定义
重载操作符必须具有至少一个类类型或枚举类型的操作数。这条规则强调重载操作符不能重新定义用于内置类型对象的操作符的含义。
作为类成员的重要函数,其形参看起来比操作数数目少1.作为成员函数的操作符有一个隐含的this形参,限定为第一个操作数。重载一元操作符如果作为成员函数就没有(显式)形参,如果作为非成员函数就有一个形参。类似的重载二元操作符定义为成员时有一个形参,定义为非成员函数时有两个形参。一般将算术和关系操作符定义为非成员函数,将赋值操作符定义为成员。
操作符定义为非成员函数时,通常必须将它们设置为所操作类的友元,因为操作符通常要访问类的私有部分。
使用重载操作符:方式一:cout<<item1+item2<<endl; 此表达式隐式调用Sales_item类而定义的operator+。
方式二:cout<<operator+(item1,item2)<<endl;
重载操作符的设计:
1.1 不要重载具有内置含义的操作符。
重载逗号、取地址、逻辑与、逻辑或等操作符通常不是好做法。这些操作符具有有用的内置含义,如果我们定义了自己的版本,就不能再使用这些内置含义。
1.2 大多数操作符对类对象没有意义
1.3 符合赋值操作符
如果提供了+操作符,那么逻辑上也应该定义+=。
1.4 相等和关系操作符
如果定义了相等操作符,也应该定义不等操作符!=.如果定义了<,则应该定义全部的(<,>=,>,<=)
1.5 选择成员或非成员实现
- =,[],(),->操作符必须定义为成员,将这些定义为非成员函数将在编译时标记为错误。
- 复合赋值通常应该定义为类的成员。
- 改变对象状态或与给定类型紧密联系的其他一些操作符,如自增、自减和解引用,通常定义为类成员。
- 对称的操作符,如算法操作符、相等操作符、关系操作符和位操作符,最好定义为普通非成员函数。
2. 输入和输出操作符
输出操作符<<的重载
class StudentInfo {
public:
friend ostream& operator<<(ostream& os,const StudentInfo&); ->先定义为友元
ostream& operator<<(ostream& os,const StudentInfo& studentInfo)
{
os<<studentInfo.aName<<'t'<<studentInfo.aAge;
return os;
}
一般而言,输出操作符应输出对象的内容,进行最小限度的格式化,它们不应该输出换行符。
IO操作符必须为非成员函数
我们不能将该操作符定义为类的成员,否则左操作符将只能是该类型的对象,这个跟其他类型定义的输出操作符正好相反。通 常将IO操作符定义为类的友元。
3. 算术操作符和关系操作符
一般将算法和关系操作符定义为非成员函数,为了与内置操作符保持一致,加法返回一个右值,而不是一个引用。算术操作符通常产生一个新值,该值是一个操作符的计算结果,它不同于任一个操作数且在一个局部变量中计算,返回对那个变量的引用是一个运行时错误。
3.1 相等操作符
- 如果定义了++操作符,该操作符的含义是两个对象包含相同的数据。
- 如果类有一个操作,来确定该类型的两个对象是否相等,通常将该函数定义为operator==
- 如果定义了operator==,也应该定义operator!=;
- 相等和不相等一般应该相互联系起来定义,让一个操作符来完成实际工作,另一个操作符只是调用前者。
定义了operator==的类更容易与标准库一起使用,有些算法,如find,默认使用==操作符,如果类定义了==,则这些算法无须任何特殊处理而用于该类类型。
3.2 关系操作符
如果<的逻辑定义与==的逻辑定义不一致,所有根本不定义<会更好。
4 赋值操作符
类赋值操作符必须是类的成员,以便编译器可以知道是否需要合成一个。一般而言,赋值操作符与复合赋值操作符应该返回左操作符的引用。return *this。
5. 下标操作符
类定义下标操作符时,一般需要定义两个版本,一个为非const成员并返回引用,另一个为const成员并返回const引用。
int &operator[]{const size_t};
const int &operator[]{const size_t} const;
6. 成员访问操作符
为了支持指针型类,例如迭代器,C++语言允许重载解引用操作符*(必须定义为类成员函数)和箭头操作符->(作为成员一般也是正确的).
7. 自定义操作符和自减操作符
自增(++)和自减(--)操作符经常有诸如迭代器这样的类实现,这样的类提供类似于指针的行为来访问序列中的元素。
C++语言不要求自增操作符或自减操作符一定作为类的成员,但是,如果这些操作符能改变操作对象的状态,所以更倾向于将它们作为成员。
前缀 CheckedPtr& operator++();
CheckedPtr& operator--();
为了与内置类型一致,前缀式操作符应返回被增量或减量对象的引用。++curr;return *this;
后缀 接收一个额外的(无用的)int型形参。使用后缀时,编译器提供0作为整个形参的实参。
CheckedPtr& operator++(int);
CheckedPtr& operator--(int);
后缀操作符应返回旧值,应作为值返回,而不是返回引用。
checkedPtr ret(*this);
--*this;
return ret;
显式调用前缀式操作符:
parr.operator++();-->前缀++
parr.operator++(0);-->后缀++
8 调用操作符和函数对象
函数调用操作符必须声明为成员函数,一个类可以定义函数操作符的多个版本,由形参的数量和类型加以区别。
定义了调用操作符的类,其对象常称为函数对象,即它们是行为类似函数的对象。
8.1 将函数对象用于标准库算法:函数对象可以比函数更灵活
class GT_cls {
public:
GT_cls(size_t val=0):bound(val){}
bool operator()(const std::string& s){
return s.size() > bound;
}
private:
std::string::size_type bound;
};
vector<string>::size_type wc=count_if(words.begin(),words.end(),GT_cls(5));
8.2 标准库定义的函数对象
#include<functional>
类型 函数对象 所应用的操作符
算术函数对象类型 plus<Type> +
minus<Type> -
关系函数对象类型 equal_to<Type> ==
greater<Type> >
plus<int> intadd;
int sum=intadd(10,20); #sum=30
在算法中使用标准库函数对象
函数对象常用于覆盖算法使用的默认操作符,例如sort默认使用operator< 按照升序对容器进行排序。
sort(svec.begin(),svec.end(),greater<string>());按照降序对vector排序。
8.3 函数对象的函数适配器
标准库提供了一组函数适配器用来特化和扩展一元和二元函数对象,函数适配器分为如下两类:
- 绑定器 是一种函数适配器,它通过将一个操作数绑定到给定值而将二元函数对象转为一元函数对象
- 求反器 是一种函数适配器,它将谓词函数对象的真值求反。
标准库定义了两个绑定适配器:bind1st和bind2nd。bind1st将给定值绑定到二元函数对象的第一个实参。
bind2nd将给定值绑定到二元函数对象的第二个实参。
count_if(vec.begin(),vec.end(),bind2nd(less_equal<int>(),10));调用计算输入范围中小于或等于10的元素的个数。
定义了两个求反器:not1和not2.not1将一元函数对象的真值求反,not2将二元函数对象的真值求反。
list<int>::size_type gc=count_if(ilist0.begin(),ilist0.end(),not1(bind2nd(less_equal<int>(),2)));
计算输入返回不<=2的元素统计值
9 转换与类类型
暂时省略
最后
以上就是落寞毛巾为你收集整理的C++ 重载操作符与转换的全部内容,希望文章能够帮你解决C++ 重载操作符与转换所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复