第14章的内容过一遍就可以了,通过重载运算符实现的方法,定义函数也可以实现。
重载运算符类型 | 建议定义在哪里 | 返回值类型 | 参数类型 | 注意事项 |
---|---|---|---|---|
<< | 非成员 | ostram& | ostream&,const CLS& | |
>> | 非成员 | istream& | istream&,CLS& | 需要考虑输入流错误的情况,一般将对象置为空 |
算术运算符 | 非成员 | CLS | const CLS&,const CLS& | 定义了+,一般就需要定义+=,定义了-,一般就需要定义-= |
关系运算符 | 非成员 | bool | const CLS&,const CLS& | 通常定义了==就需要定义!=,定义了<就需要定义>,<=,>= |
赋值,复合赋值运算符 | 成员 | CLS& | const CLS& | |
下标运算符 | 成员 | CLS& | size_t | 一般需要定义一个const和一个非const版本 |
前置递增递减 | 成员 | CLS& | - | |
后置递增递减 | 成员 | CLS | int | int形参只用来区别前置和后置,一啊不能不会用到这个参数 |
解引用* | 成员 | CLS& | - | |
箭头访问符 | 成员 | CLS* | - | 箭头访问符一定要访问一个类的成员,如p->size()否则报错 |
14.1 基本概念
我们可以重载大部分的运算符,重载运算符可以提高代码的简洁程度。
重载运算符需要operator+运算符的名字来重载运算符。
下面是可以重载的运算符。
调用运算符其实是调用函数,所以我们可以重载的运算符,一样的需要返回值,形参列表,函数体。但是不能类型的运算符返回值和参数个数是不一样的
bool operator+(A&,A&){
}
A& A::operator+=(A&){}
{
}
因为重载运算符实际上就是一个函数,拿在外部重载的+作为例子,我们可以这样做,只不过这样做不好看。
a+b;
operator+(a,b);
重载之后的运算符优先级还是和内置的一样。
重载的运算符可以作为成员函数也可以作为普通函数,如果是成员函数则运算符的第一个或者左侧的运算符对象将绑定到*this上。所以写在类中的重载运算符将会少一个参数。
具体什么时候定义在类中,什么时候定义在外部,则看具体的使用情况。
书上是这样说的:
可以看到有一些必须定义在类中,有一些则类内,类外都可以。
还有一些对称性的运算符,则需要定义在外部。
练习
14.1
如果重载的运算符为成员函数,那么运算符左侧或者第一个运算对象将绑定到*this上。对于算数,关系,==等对称性运算符,其左侧(第一个)运算对象必须为类类型。
如果重载的运算符不是成员函数,则操作规则和内置类型一样。
14.2
加法
Sales_data operator+(Sales_data& data1, Sales_data& data2) {
if (data1.isbn() == data2.isbn())
{
data1.combine(data2);
}
return data1;
}
复合赋值
inline Sales_data & Sales_data::operator+=(Sales_data & data)
{
// TODO: 在此处插入 return 语句
if (data.isbn()==isbn())
{
combine(data);
}
return *this;
}
14.3
这里我认为定义了重载的==,是在成员函数内部重载的。
a。调用系统内置的==
b。调用string的
c。调用vector的
d。调用string的
14.4
a。左右对象都可以是运算符对象,所以不建议定义为成员函数
b。应该
c。应该
d。应该
e。从后面的内容得知不应该
f。&&不应该
g,不应该
h,应该
14.5
我写的是Date。
一般不用重载运算符,因为如果定义+,2010/1/22+2010/2/22等于多少呢?没有统一的标准,所以重载运算符,而是提供add_month(),add_day()这样的接口更好。
当然我们可以定义两个Date相减得到一个日期。
这里就不具体实现了。
14.2 输入和输出运算符
重载输入和输出运算符定义为非成员函数。
通常重载的输入运算符需要处理输入流错误(异常)的情况,通常的做法是将对象的状态置为空。
练习
14.6,14.7,14.8 形式都是一样的,不再重复
ostream& operator<<(ostream& os,const Sales_data& data) {
os << data.bookNo << " " << data.units_sold << " " << data.revenue;
return os;
}
14.9
istream& operator>>(istream& is, Sales_data & data)
{
double price;
is >> data.bookNo >> data.units_sold >> price;
//is >> data.units_sold;
if (is)
{
data.revenue = data.units_sold*price;
}
else {
data = Sales_data();
}
return is;
// TODO: 在此处插入 return 语句
}
14.10
a.得到一个正确的对象
b。因为price为double类型,而输入的是字符串,所以流的状态变为异常,所以得到一个空对象
14.11
存在,这个函数没有处理输入流错误时的情况
发生未定义了的行为,此时bookNo和units_sold都已经被赋值,但是price的值将是默认的值(如果设有默认值的话)
14.12
istream & operator>>(istream & is, My_Date & date)
{
is >> date.year >> date.month >> date.day;
if (!is)
{
date = My_Date();
}
return is;
// TODO: 在此处插入 return 语句
}
14.3 算术和关系运算符
练习
14.13
可以支持==运算符
略,这些操作做一个熟悉一下就可以了
14.14
因为+=返回的是引用不用创建对象
14.15
两个日期并不好做算术运算,可以相减得到一个具体天数,但是其返回值类型并不是MyDate,这样就违背了尽量不改变运算符原本意义的初衷。
14.16
略
14.17
bool operator==(const My_Date & d1, const My_Date & d2)
{
return d1.year==d2.year&&d1.month==d2.month&&d1.day==d2.day;
}
bool operator!=(const My_Date & d1, const My_Date & d2)
{
return !(d1==d2);
}
14.18
略
14.19
定义了小于和大于,注意,在判断大于时,需要判断是否小于等于
bool operator<(const My_Date & d1, const My_Date & d2)
{
bool flag = true;
if (d1.year>d2.year)
{
flag = false;
}
else if (d1.year==d2.year&&d1.month>d2.month) {
flag = false;
}
else if (d1.year == d2.year&&d1.month==d2.month&&d1.day>d2.day) {
flag = false;
}
return flag;
}
bool operator>(const My_Date & d1, const My_Date & d2)
{
return (d1!=d2)&&!(d1<d2);
}
14.4 赋值运算符
练习
14.20
略
14.21
+=中调用+,因为+返回一个非引用类型的对象就意味着需要构建一个临时对象。
14.22,14.23
略
14.24
因为My_Date中不需要管理其他的资源,没有指针类型。所以移动赋值对其作用不大。
My_Date(const My_Date& d) :year(d.year), month(d.month), day(d.day) {};
14.25
实现复合赋值的意义不大,这里的运算对象左值右值类型都可以。
因为const的引用既可以绑定到左值上也可以绑定到右值上
My_Date & My_Date::operator=(const My_Date & d)
{
year = d.year;
month = d.month;
day = d.day;
// TODO: 在此处插入 return 语句
return *this;
}
14.26
string & StrBlobPtr::operator[](size_t index)
{
// TODO: 在此处插入 return 语句
auto p =check(curr+index, "index out of range");
return (*p)[curr+index];
}
string & StrVec::operator[](const size_t index)
{
// TODO: 在此处插入 return 语句
return *(elements + index);
}
string & StrVec::operator[](const size_t index) const
{
// TODO: 在此处插入 return 语句
return *(elements + index);
}
14.6 递增递减运算符
练习
14.27,14.28
StrBlobPtr & StrBlobPtr::operator++()
{
check(curr, "index out of rage");
++curr;
return *this;
// TODO: 在此处插入 return 语句
}
StrBlobPtr & StrBlobPtr::operator--()
{
// TODO: 在此处插入 return 语句
--curr;
check(curr, "index out of rage");
return *this;
}
StrBlobPtr StrBlobPtr::operator++(int)
{
auto temp = *this;
++*this;
return temp;
// TODO: 在此处插入 return 语句
}
StrBlobPtr StrBlobPtr::operator--(int)
{
auto temp = *this;
--*this;
return temp;
// TODO: 在此处插入 return 语句
}
StrBlobPtr StrBlobPtr::operator+(size_t v)
{
//检查加上一个值之后是否越界
check(curr + v, "add size cause index out of range");
StrBlobPtr ptr = *this;
ptr.curr = curr + v;
return ptr;
}
StrBlobPtr StrBlobPtr::operator-(size_t v)
{
check(curr - v, "sub size cause index out of range");
StrBlobPtr ptr = *this;
ptr.curr = curr - v;
return ptr;
}
14.29
因为递增递减需要改变对象本身,定义为const版本就不能改变自身了。
14.7 成员访问运算符
练习
14.30
string & StrBlobPtr::operator*()const
{
// TODO: 在此处插入 return 语句
auto ret = check(curr, "point out of range");
return (*ret)[curr];
}
//箭头元素运算符,必须访问一个成员
string * StrBlobPtr::operator->()const
{
auto ret = check(curr, "point out of range");
return &(*ret)[curr];
}
const string & ConstStrBlobPtr::operator*()const
{
// TODO: 在此处插入 return 语句
auto ret = check(curr, "point out of range");
return (*ret)[curr];
}
const string * ConstStrBlobPtr::operator->()const
{
auto ret = check(curr, "point out of range");
return &(*ret)[curr];
}
14.31
因为StrBlobPtr内部管理资源的成员是weak_ptr一种智能指针,它指向了一个shared_ptr管理的对象,shard_ptr可以自动的管理内存,所以我们不必定义拷贝控制函数。
14.32
temp对象通过箭头访问符,访问StrBolbPtr的->访问符。strblobptr的箭头访问符,返回string类型的指针,通过该指针访问string的size()函数。
struct Temp
{
StrBlobPtr* operator->();
StrBlobPtr* p;
};
cout<<temp->operator->()->size()<<endl;