重载运算与类型转换
基本概念
- 重载的运算符是具有特殊名字的函数,operator运算符号
- 返回类型、参数列表、函数体
- 如果一个运算符函数是成员函数,则其第一个运算对象绑定到隐式的this指针上,因此成员运算符函数的(显式)参数数量比运算符的运算对象总数少一个
- 运算符函数或者是类的成员,或者至少含有一个类类型的参数
//int operator+(int,int);
//不能为int重定义内置运算符
直接调用一个重载的运算符函数
data1 + data2;
operator+(data1,data2);
data1 += data2;
data1.operator += (data2);
输入和输出运算符
重载输出运算符
- 第一个形参:非常量ostream对象的引用
- 非常量:向流写入内容会改变其状态
- 引用:无法直接赋值一个ostream对象
- 第二个形参:常量的引用
- 引用:希望避免赋值实参
- 常量:通常打印对象不会改变对象的内容
- 输出运算符应尽量减少格式化操作
ostream &operator<<(ostream &os,const Sales_data &item){
os<<item.isbn()<<" "<<item,units_sold<<" "<<item,revenue<<" "<<item.avg_price();
return os;
}
- 输入输出运算符必须是非成员函数,否则其左侧的运算对象将是类的一个对象
- 由于IO运算符需要读写类的非公有数据成员,通常将IO运算符声明为友元
重载输入运算符
- 第一个形参:运算符将要读取的流的引用
- 第二个形参:将要读入到的(非常量)对象的引用
istream &operator>>(istream &is,Sales_data &item){
double price;
is >> item.bookNo>>item.units_sold>>price;
if(is)//检查输入是否成功
item.revenue = item.units_sold * price;
else
item = Sales_data();
return is;
}
- 输入运算符必须处理输入可能失败的情况,而输出运算符不需要
输入时的错误
- 当流含友错误类型的数据时读取操作可能失败
- 当读取操作到达文件末尾或者遇到输入流的其他错误时也会失败
算术和关系运算符
- 通常将算术和关系运算符定义为非成员函数以允许对左侧或右侧的运算对象进行转换
- 形参都是常量的引用
Sales_data operator+(const Sales_data &lhs,const Sales_data &rhs){
Salte_data sum = lhs;
sum += rhs;
return sum;
}
相等运算符
bool operator==(const Sales_data &lhs,const Sales_data &rhs){
return lhs.isbn()==rhs.isbn()&&lhs.units_sold==rhs.units_sold &&lsh.revenue==rhs.revenue;
}
bool operator!=(const Sales_data &lhs,const Sales_data &rhs){
return !(lhs==rhs);
}
- 如果类定义了operator==,也应该定义operator!=
赋值运算符
vector<string>v;
v={"a","b","c"};
class StrVec{
public:
StrVec &operator=(std::initializer_list<std::string>);
};
StrVec &StrVec::operator=(initializer_list<string> il){
//alloc_n_copy分配内存空间并从给定范围内拷贝元素
auto data = alloc_n_copy(il.begin(),il.end());
free();
elements=data.first;
first_free=cap=data.second;
return *this;
}
下标运算符
class StrVec{
public:
std::string& operator[](std::size_t n){
return elements[n];
}
const std::string& operator[](std::size_t n)const
{return elements[n];}
}
递增递减运算符
定义前递增/递减运算符
class StrBlobPtr{
public:
//递增和递减运算符
StrBlobPtr& operator++();//前置运算符
StrBlobPtr& operator--();
};
StrBlobPtr& StrBlobPtr::operator++(){
//如果curr已经指向容器的尾后位置,则无法递增它
check(curr,"increment pasr end of StrBlobPtr");
++curr;
return *this;
}
StrBlobPtr& StrBlobPtr::operator--(){
//如果curr是0则继续递减它将产生一个无效下标
--curr;
check(curr,"decrement past begin of StrBlobPtr");
return *this;
}
区分前置和沪指运算符
- 后置版本接受一个额外的(不被使用)int类型的形参
class StrBlobPtr{
public:
//递增和递减运算符
StrBlobPtr operator++(int);//前置运算符
StrBlobPtr operator--(int);
};
StrBlobPtr StrBlobPtr::operator++(int){
//如果curr已经指向容器的尾后位置,则无法递增它
StrBlobPtr ret = *this;
++*this;
return ret;
}
StrBlobPtr StrBlobPtr::operator--(){
StrBlobPtr ret = *this;
--*this;
return ret;
}
成员访问运算符
class StrBlobPtr{
public:
std::string& operator*() const
{
auto p = check(curr,"dereference past end");
return (*p)[curr];
}
std::string* operator->() const
{
return & this->operator*();
}
}
函数调用运算符
struct absInt{
int operator()(int val)const{
return val<0?-val:val;
}
};
int i = -42;
absInt absObj;
int ui = absBoj(i);
含有状态的函数对象类
class PrintString{
public:
PrintString(ostream &o = cout,char c = ' '):
os(o),sep(c){}
void operator()(const string &s)const {os<<s<<sep;}
private:
ostream &os;
char sep;
}
重载、类型转换与运算符
类型转换运算符
- operator type() const;
- 一个类型转换函数必须是类的成员函数,不能声明返回类型,形参列表必须为空
class SmallInt{
public:
SmallInt(int i = 0):val(i)
{
if(i<0||i>255)
throw std::out_of_range("Bad SmallInt value");
}
operator int() const {return val;}
private:
std::size_t val;
}