//注意:这章的练习我将会十分简略地完成。。。
练习14.1:在什么情况下重载的运算符和内置运算符有所区别?在什么情况下重载的运算符又与内置运算符一样?
答:(标准)
不同点:
重载操作符必须具有至少一个class或者枚举类型的操作数。
重载操作符不保证操作数的求值顺序。
相同点:
对于优先级和结合性及操作数的数目都不变。
练习14.2:为Sales_data类编写重载的输入、输出、加法和复合赋值运算符。
PS:其中>> 、<<和+运算符定义为非成员函数。
+=定义为成员函数。
istream& Sales_data::operator>>( istream &is, Sales_data &rhs ){
is >> rhs.bookNo;
is >> rhs.units_sold;
is >> rhs.selling_price;
is >> rhs.sales_price;
if( is && !selling_price )
discount = sales_price / selling_price;
else
rhs = Sales_data();
return is;
}
ostream& Sales_data::operator<<( ostream &os, const Sales_data &rhs ){
os << rhs.bookNo << " "
<< rhs.units_sold << " "
<< rhs.selling_price << " "
<< rhs.sales_price << " "
<< rhs.discount; //不添加换行,换行自行添加,增加操作的灵活性。
return os;
}
Sales_data& Sales_data::operator+=( const Sales_data &rhs ){
//同一本书的零售价应该是相同的,所以selling_price应该相同。
sales_price = ( sales_price * units_sold + rhs.sales_price * rhs.units_sold )
/ ( units_sold + rhs.units_sold );
units_sold += rhs.units_sold;
if( selling_price != 0 )
discount = sales_price / selling_price;
return *this;
}
Sales_data Sales_data::operator+( const Sales_data &lhs, const Sales_data &rhs ){
//默认认为两个Sales_data对象的bookNo相同
Sales_data tmp( lhs );
tmp += rhs;
return tmp;
}
练习14.3:string和vector都定义了重载的==以比较各自的对象,假设svec1和svec2是存放string的vector,确定在下面的表达式中分别使用了哪个版本的==?
(a) "cobble" == "stone"
直接比较的是字符串首元素的地址。内置版本的==
(b)svec1[0] == svec2[0]
调用的是string的重载的==运算符
(c)svec1 == svec2
调用了vector的重载的==运算符
(d)svec1[0] == "stone"
调用了string的重载的==运算符。字面字符常量被转换为string
练习14.4:如何确定下列运算符是否应该是类的成员?
(a) %
非成员。
(b)%=
成员
(c)++
成员
(d)->
必须是类成员
(e)<<
必须是非成员
(f)&&
非成员。但不应该重载(因为不能保证&&的短路求值属性)
(g)==
非成员
(h) ()
必须是类成员
练习14.5:在7.5.1节的练习7.40中,编写了下列类中某一个的框架,请问在这个类中应该定义重载的运算符吗?如果是,请写出来。
对于book类:
可以重载 >>输入运算符和<<输出运算符。
练习14.6:为你的Sales_data类定义输出运算符。
见练习14.2。
练习14.7:你在13.5节的练习中曾经编写了一个String类,为它定义一个输出运算符。
注意类内还要加上友元声明。
ostream& operator<<( ostream &os, const String &s )
// C++定义了输出运算符接受字符指针输出字符串的版本
os << s.p;
return os;
}
class book{
friend ostream& operator<<( ostream&, const book& );
private:
string bookName;
string Author;
string ISBN;
string Publisher;
double price = 0.0;
public:
book() = default;
book( std::istream &is ) { is >> *this; }
book( const string &n, const string &a, const string &I, const string &P, double pr );
};
ostream& operator<<( ostream &os, const book &bk ){
os << "《" << bk.bookName << "》" << " "
<< bk.Author << " "
<< bk.ISBN << " "
<< bk.Publisher << " "
<< "¥" << bk.price;
return os;
}
练习14.9:为你的Sales_data类定义输入运算符。
见练习14.2。
练习14.10:对于Sales_data的输入运算符来说如果给定了下面的输入将发生什么情况。
(a)正常读入一个Sales_data对象。
(b)输入错误,将得到一个Sales_data对象的默认值
练习14.11:下面的Sales_data输入运算符存在错误吗?如果有,请指出来。对于这个输入运算符如果仍然给定上个练习的输入将发生什么情况。
有错误。题目中的输入运算符定义中没有读取输入后的流进行错误性判断。如果输入错误,那么这个类对象将得到错误数据的数据成员。
如果给定上面联系的输入:
对于(a),输入正常,将正常读入一个Sales_data对象。
对于(b),输入错误,导致数据成员“张冠李戴”。bookNo读入成为10, units_sold读入为24,revenue为24*0.95;此时流的failbit被置位。
练习14.12:你在7.5.1节的练习7.40中国曾经选择并编写了一个类,为它定义一个输入运算符并确保该运算符可以处理输入错误。
#ifndef BOOK_H_INCLUDED
#define BOOK_H_INCLUDED
#include
#include
using namespace std;
class Book{
//友元声明
friend istream& operator>>( istream&, Book& );
friend ostream& operator<<( ostream&, const Book& );
public:
//默认构造函数
Book() = default;
//其他构造函数
Book( std::istream& );
Book( const string &bk, const string &a, const string &I, const string &P, double pr ):
bookName(bk), Author(a), ISBN(I), Publisher(P), price(pr) { }
private:
string bookName;
string Author;
string ISBN;
string Publisher;
double price = 0.0;
};
//友元定义
istream& operator>>( istream &is, Book &bk ){
is >> bk.bookName >> bk.Author
>> bk.ISBN >> bk.Publisher
>> bk.price;
//如果流检测到错误,将对象置为默认状态
if( !is )
bk = Book();
return is;
}
ostream& operator<<( ostream &os, const book &bk ){
os << "《" << bk.bookName << "》" << " "
<< bk.Author << " "
<< bk.ISBN << " "
<< bk.Publisher << " "
<< "¥" << bk.price;
return os;
}
Book::Book( istream &is ){
is >> *this;
}
#endif // BOOK_H_INCLUDED
练习14.13:你认为Sales_data类还应该支持哪些其他算术运算符?如果有的话,请给出它们的定义。
综合考虑下应该只需要+、+=、<<、 >>运算符。
练习14.14:你觉得为什么调用operator+=来定义operator+比其他方法更有效?
使用+=可以使得代码更加简洁,而且可读性也更好。
练习14.15:你在7.5.1节的练习7.40中曾经选择并编写了一个类,你认为它应该含有其他算术类型吗?如果是,请实现它们;如果不是,解释原因。
我选择的类是book,我认为不需要含有其他算术类型。因为其中的5个数据成员中书名,作者名,书号,出版社都不适合算术运算,而剩下的价格成员,它的运算也是没有意义的。
练习14.16:为你的StrBlob类、StrBlobPtr类、StrVec类和String类分别定义相等运算符和不相等运算符。
略了。
练习14.17:你在7.5.1节的练习中曾经选择并编写了一个类,你认为它应该含有相等运算符吗?如果是,请实现它;如果不是,解释原因。
我认为需要。5个数据成员中,我认为只需比较其中两个:ISBN 和 price。因为ISBN相同则书名作者肯定也相同,(价格如果是零售价的话:应该也相同)如果价格是实际售价的话,有可能不同,所以价格也需要比较。
代码略了。
练习14.18:为你的StrBlob类、StrBlobPtr类、StrVec类和String类定义关系运算符。
略了。
练习14.19:你在7.5.1节的练习7.40中曾经选择并编写了一个类,你认为它应该含有关系运算符吗?如果是,请实现它;如果不是,解释原因。
可以根据实际需要编写关系运算符:
根据书名、ISBN、作者名、甚至出版社名比较字典序大小应该都可以,也可以根据书的价格大小比较。
练习14.20:为你的Sales_data类定义加法和复合赋值运算符。
见练习14.2。
练习14.21:编写Sales_data类的+和+=运算符,使得+执行实际的加法操作而+=调用+。相比于之前的定义,本题的定义有何缺点?试讨论之。
代码就略了。
性能上,我认为+=运算符会做了冗余的工作。需要用到+运算符返回的值再调用拷贝赋值运算符,而+运算符本身已经构造过一个临时的Sales_data类对象了。
代码上,我认为代码可读性降低了,而且编写起来也更繁杂。
练习14.22:定义赋值运算符的一个新版本,使得我们能把一个表示ISBN的string赋值给一个Sales_data对象。
Sales_data& Sales_data::operator=( const string &str ){
bookNo = str;
return *this;
}
略了。
练习14.24:你在7.5.1节的练习7.40中曾经选择并编写了一个类,你认为它应该含有拷贝赋值和移动赋值运算符吗?如果是,请实现它们。