《C++ Primer》第14章 操作重载与类型转换
本章介绍了运算符重载的基本概念,介绍了各种运算符的重载方式,并介绍了类型转换与运算符重载的关系。本章的练习着重让读者掌握各种运算符的重载,包括输入/输出运算符、各种算术运算符、下标运算符以及函数调用运算符等。特别是,结合自定义的Sales_data、StrBlob等较为复杂的类进行运算符重载的练习,让读者体会运算符重载是如何帮助改进代码质量的。此外,还通过一些练习帮助读者弄清类型转换与运算符重载的关系。特别是,当有多个重载版本时,在进行函数匹配时涉及类型转换应如何处理。
14.1节 基本概念 习题答案
练习14.1:在什么情况下重载的运算符与内置运算符有所区别?在什么情况下重载的运算符又与内置运算符一样?
【出题思路】
理解重载运算符与内置运算符的区别。
【解答】
不同点:
重载操作符必须具有至少一个class或枚举类型的操作数。重载操作符不保证操作数的求值顺序,例如对&&和||的重载版本不再具有“短路求值”的特性,两个操作数都要进行求值,而且不规定操作数的求值顺序。相同点:对于优先级和结合性及操作数的数目都不变。
练习14.2:为Sales_data编写重载的输入、输出、加法和复合赋值运算符的声明。
【出题思路】
本题练习重载运算符的声明。
【解答】
几个运算符的声明如下所示:
#ifndef SALES_DATA14_02_H #define SALES_DATA14_02_H #include#include class Sales_item { friend std::istream& operator>>(std::istream&, Sales_item&); friend std::ostream& operator<<(std::ostream&, const Sales_item&); friend bool operator<(const Sales_item&, const Sales_item&); friend bool operator==(const Sales_item&, const Sales_item&); public: Sales_item() = default; Sales_item(const std::string &book): bookNo(book) { } Sales_item(std::istream &is) { is >> *this; } public: Sales_item& operator+=(const Sales_item&); std::string isbn() const { return bookNo; } double avg_price() const; private: std::string bookNo; //书号 unsigned units_sold = 0; //出售册数 double revenue = 0.0; //收入 }; inline bool compareIsbn(const Sales_item &lhs, const Sales_item &rhs) { return lhs.isbn() == rhs.isbn(); } Sales_item operator+(const Sales_item&, const Sales_item&); inline bool operator==(const Sales_item &lhs, const Sales_item &rhs) { return (lhs.units_sold == rhs.units_sold) && (lhs.revenue == rhs.revenue) && (lhs.isbn() == rhs.isbn()); } inline bool operator !=(const Sales_item &lhs, const Sales_item &rhs) { return !(lhs == rhs); } Sales_item& Sales_item::operator +=(const Sales_item& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } Sales_item operator +(const Sales_item& lhs, const Sales_item& rhs) { Sales_item ret(lhs); ret += rhs; return ret; } std::istream& operator>>(std::istream& in, Sales_item& s) { double price; in >> s.bookNo >> s.units_sold >> price; if(in) s.revenue = s.units_sold * price; else s = Sales_item(); return in; } std::ostream& operator <<(std::ostream& out, const Sales_item& s) { out << s.isbn() << " " << s.units_sold << " " << s.revenue << " " << s.avg_price(); return out; } double Sales_item::avg_price() const { if(units_sold) return revenue / units_sold; else return 0; } #endif // SALES_DATA14_02_H
#include "Sales_data14_02.h" #includeusing namespace std; int main() { Sales_item trans1, trans2; std::cout << "请输入两条ISBN相同的销售记录:" << std::endl; std::cin >> trans1 >> trans2; if(compareIsbn(trans1, trans2)) std::cout << "汇总信息:ISBN,售出本数,销售额和平均售价为: " << trans1 + trans2 << std::endl; else std::cout << "两条销售记录的ISBN不同" << std::endl; cout << "Hello World!" << endl; return 0; }
运行结果:
练习14.3:string和vector都定义了重载的==以比较各自的对象,假设svec1和svec2是存放string的vector,确定在下面的表达式中分别使用了哪个版本的==?
(a) "cobble" == "stone" (b) svec1[0] == svec2[0] (c) svec1 == svec2 (d) svec1[0] == "stone"
【出题思路】
本题旨在理解编译器如何选择重载运算符的不同版本。
【解答】
(a)"cobble" == "store"应用了C++语言内置版本的==,比较两个指针。
(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) ()
【出题思路】
理解编译器如何选择重载运算符的不同版本。
【解答】
(a)%通常定义为非成员。
(b)%=通常定义为类成员,因为它会改变对象的状态。
(c)++通常定义为类成员,因为它会改变对象的状态。
(d)->必须定义为类成员,否则编译会报错。
(e)<<通常定义为非成员。
(f)&&通常定义为非成员。
(g)==通常定义为非成员。
(h)()必须定义为类成员,否则编译会报错。
练习14.5:在7.5.1节的练习7.40(第261页)中,编写了下列类中某一个的框加,请问在这个类中应该定义重载的运算符吗?如果是,请写出来。
(a) Book (b) Date (c) Employee
(b) Vehicle (e) Object (f) Tree
【出题思路】
学会判断是否需要为类定义重载运算符。
【解答】
以(b)Date为例,为其定义重载的输出运算符,输入运算符可参照实现。显然,为Date定义输入输出运算符,可以让我们像输入输出内置类型对象那样输入输出Date,在易用性和代码的可读性上有明显的好处。因此,定义这两个重载运算符是合理的。
#includeusing namespace std; class Date { friend ostream& operator<<(ostream &os, const Date &dt); public: Date(); Date(int y, int m, int d) { year = y; month = m; day = d; } private: int year; int month; int day; }; ostream& operator<<(ostream& os, const Date& d) { const char sep = 't'; os << "year:" << d.year << sep << "month:" << d.month << sep << "day:" << d.day << endl; return os; } int main() { Date date(2029, 8, 6); cout << "date==========" << date << endl; cout << "Hello World!" << endl; return 0; }
运行结果:
date==========year:2029 month:8 day:6



