重载操作符必须至少一个类类型或枚举类型的操作数。这条规则强制重载操作符不能重新定义用于内置类型对象的操作的含义。
重载操作符时不能使用默认实参。
重载操作符的设计
1.不要重载具有内置含义的操作符
重载&& || 或逗号或取地址不是一种好的做法。如果定义了,则不能使用内置含义
2.多数操作符对类对象没有意义
为类设计操作符,最好的方式是首先设计类的公用接口。定义接口后,就可以考虑应将哪些操作符定义为重载操作符
3。复合赋值操作符
如果一个类有算术操作符或位操作符,那么,提供响应的复合赋值操作符一般是一个好的做法。
4.相等或关系操作符
将要用作关联容器建类型的类应定义 < 操作符。即使该类型将只存储在顺序容器中,类通常也定义相等操作符和 < 操作符,理由是许多算法假定这些操作符存在。
5.选择成员或非成员实现
a。赋值,下标(【】),调用(()),成员访问箭头(->),转换操作符等操作符必须定义为成员,如果为非成员,编译不通过
b。像赋值一样,复合赋值操作符通常应定义为类的成员
c。改变对象状态或与给定类型紧密联系的其他一些操作符。如自增,自减和解引用,通常定义为类成员
d。对称的操作符,如算术操作符,相等操作符,关系操作符,位操作符,最好定义为普通非成员函数
e。输出,输入只能定义为非成员操作符,如果为成员,编译不同。
详细实现
1.输入和输出操作符
a。输出操作符<<的重载
为了与IO标准库一致,操作符应接受ostream& 作为第一个形参,对类类型const 对象的引用作为第二个形参,并返回对ostream&
b。输入操作符>>的重载
为了与IO标准库一致,操作符应接受istream& 作为第一个形参,对类类型必须为非const 对象的引用作为第二个形参,并返回对istream&
输入操作符必须处理错误和文件结束的可能性,如果可能,要确定错误恢复措施,有些操作符的确需要进行附加检查
istream& operator>> (istream& in, Sales_item &s)
{
double price;
in >> s.isbn >> s.units_sold >> price;
//check that the inputs succeed
if(in)
s.revenue = s.units_sold * price;
else
//input failed: rest object to defalut state;
s = Sales_item();
}
2.算术操作符和关系操作符
操作数是对const对象的引用,相反,返回一个右值,而不是一个引用。
一般应用复合赋值实现算术操作符
// assumes that both objects refer to the same isbn
inline
Sales_item& Sales_item::operator+=(const Sales_item& rhs)
{
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
// assumes that both objects refer to the same isbn
inline
Sales_item
operator+(const Sales_item& lhs, const Sales_item& rhs)
{
Sales_item ret(lhs); // copy lhs into a local object that we'll return
ret += rhs; // add in the contents of rhs
return ret; // return ret by value
}
3.相等操作符
4.关系操作符
5.赋值操作符
6.下标操作符
一般需要定义二个版本:一个定义为非const成员并返回引用,另一个为const成员并返回const引用
int& operator[] (const size_t);
const int& operator[] (const size_t);
7.成员访问操作符
一般需要定义二个版本:一个定义为非const成员并返回引用,另一个为const成员并返回const引用
Screen& operator* () { return *ptr->sp }
const Screen& operator* () const { return *ptr->sp }
Screen* operator-> () { return ptr->sp }
const Screen* operator-> () const { return ptr->sp }
8.自增和自减操作符
定义前自增/前自减操作符
为了与内置类型一致,前缀式操作符应返回被增量或减量对象的引用
CheckedPtr& CheckedPtr::operator++()
{
if(curr == end)
throw out_of_range("increment past the end of CheckedPtr");
++curr;
return *this;
}
CheckedPtr& CheckedPtr::operator--()
{
if(curr == end)
throw out_of_range("increment past the end of CheckedPtr");
--curr;
return *this;
}
定义后自增/后自减操作符
后缀式操作符函数接受一个额外(即,无用的)的int型形参,编译器提供0作为这个形参的实参
CheckedPtr& CheckedPtr::operator++(int)
{
CheckedPtr ret(*this);
++*this;//调用这个对象的CheckedPtr前缀自增操作符,检查自增是否安全并将curr加1或抛出异常。
return ret;
}
CheckedPtr& CheckedPtr::operator--(int)
{
CheckedPtr ret(*this);
--*this;// .............
return ret;
}
显示调用
parr.operator++(0);//后缀式
parr.operator++();//前缀式
一般而言,最好前缀式和后缀式都同时要定义
9.调用操作符和函数对象
概念:定义了调用操作符的类,其对象常叫做 函数对象,即他们是行为类似函数的对象。
函数对象经常用作通用算法的实参
bool GT6(const string &s)
{
return s.size() >= 6;
}
vector<string>::size_type wc = count_if(words,begin(), words.end(),GT6);
将上述代码修改成通用的算法
class GT_cls{
public:
GT_cls(size_t val = 0) : bound(val) { }
bool operator() (const string &s) { return s.size() >= bound; }
private;
std::string::size_t bound;
};
cout << count_if(words.begin(), words.end(), GT_cls(6));
这里看出函数对象比函数更灵活。
下面总结标准库定义一组算术,关系,与逻辑的函数对象类,每个函数对象类都是类模版。使用时加functional头文件
plus<int> intAdd;
negate<int> int intNegate;
int sum = intAdd(1,2);
sum = intAdd(10, intNegate(10));
sort(sevc.begin(), sevc.end(), greater<string>());//从大到小排序
概念:一元减和逻辑非为一元函数对象,其余二元操作符为二元函数对象。
标准库提供了一组函数适配器,用于特化和扩展一元和二元函数对象,分绑定器(bind1st,bind2nd),求反器(not1,not2)
例:
计算一个容器中所有小于或等于10的元素
count_if(vec.begin(),vec.end(), bind2nd(less_equal<int>(),10));//bind2nd绑定给定值到二元函数对象的第二个实参,bind1st绑定到第一个实参
例:
计算一个容器中所有大于或等于10的元素
count_if(vec.begin(),vec.end(),not2( bind2nd(less_equal<int>(),10)));//not2将二元函数对象的绑定求反,not1将一元函数对象的绑定求反
10.转换操作符
概念引入:当提供实参类型的对象而需要类类型的对象时,编译器将使用该转换,或则调用自己定义的复制构造函数,该过程为其他类型对象转换为类类型对象。
相反将类类型对象转化为其他类型的对象中将用到转化操作符。
转换操作符使用的最大优势:定义一个冲SmallInt类到int类型的转换,则无需定义定义任何算术,关系或等于操作符。
转化函数通用形式:
operator type();//对于任何可以作为函数返回类型(void除外)的类型都可以定义转换函数,无返回类型,形参必为空,通常定义为const成员
class SamllInt
{
public:
SmallInt(int i = 0) : val(i)
{
if(i < 0 || i >255)
{
throw std::out_of_range("Bad SmallInt initializer");
}
}
operator int() const { return val;}
private:
std::size_t val;
}
int ival;
SmallInt si =3.1415;
ival = static_cast<int>(si) + 3;
注意:只能应用一个类类型转换