14.1节
14.1答
不同点:
重载操作符必须具有至少一个class或枚举类型的操作数。
重载操作符不保证操作数的求值顺序,例如对&&和| | 的重载版本不再具有“短路求值”的特征,两个操作数,两个操作数都要进行求值,而且不规定操作数的求值顺序。
相同点:
对于优先级和结合性及操作数的数目都不变。
14.2 答:
文件sales_data.cc
#include#include #include "Sales_data.h" using namespace std; Sales_data operator+(const Sales_data &lhs,const Sales_data &rhs) { Sales_data data = lhs; data += rhs; return data; } Sales_data& Sales_data::operator+=(const Sales_data &s) { this->units_sold += s.units_sold; this->revenue += s.revenue; return *this; } istream & operator>>(istream &is,Sales_data &s) { is >> s.bookNo >> s.units_sold >> s.revenue; return is; } ostream & operator<<(ostream &os,const Sales_data &s) { os << s.bookNo << s.units_sold << s.revenue; return os; }
文件 Sales_data.h
class Sales_data {
friend Sales_data operator+(const Sales_data &,const Sales_data &);
friend istream & operator>>(istream &is,Sales_data &);
friend ostream & operator<<(ostream &os,const Sales_data &);
public:
Sales_data &operator+=(const Sales_data &);
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
注意:切记不可在友元函数的前面加Sales_data::,否则编译器回提示,C++:‘std::ostream& String::operator<<(std::ostream&, const Sales_data &)’ must take exactly one argument
14.3
答:
(a)使用了c++内置版本的const char *版本的 ‘==’运算符。
字符串字面量是 const char* 类型(b)string类型的"=="运算符
(c)vector类型的"=="运算符
(d)首先const char *类型的"stone"转换为 string,然后使用string类型的“=="
14.4
答:
(a)%通常定义为非成员,因为可以左右调换
(b)%=会改变自身状态,成员
(c)++成员
(d)->必须为成员
(e)&&非成员,一般不建议重载(&&和| |重载后,短路求值属性会失效)
(f)<<非成员,因为左操作数必须是 ostream类型。
(g)== 一般定义为非成员
(h)()必须为成员,否则编译器会报错。
赋值运算符,下标运算符,调用运算符,成员运算符必须是成员运算符,其余没有强制要求。
14.5
答:
我们以Date类为例,为其定义重载的输出运算符,输入运算符可参照实现,显然,为Date定义输入输出运算符,可以让我们像输入输出内置类型对象那样输入输出Date,在易用性和代码的可读性上有明显的好处。因此,定义这两个重载运算符是合理的。
文件date.h
#ifndef DATE_H #define DATE_H #include#include using namespace std; class Date { public: Date(){} Date(int y,int m,int d){year = y;month = m;day = d;} friend ostream & operator<< (ostream &os,const Date &dt); private: int year,month,day; }; #endif
#include#include #include "date.h" using namespace std; ostream & operator<<(ostream & os,const Date &dt) { const char sep = 't'; os << "year:"<< dt.year << sep << "month:"<< dt.month< 14.2节
14.2.1习题
习题14.6
14.7
#ifndef STRING_H #define STRING_H #include#include #include #include #include #include #include class String { friend String operator+(const String&, const String&); friend String add(const String&, const String&); friend std::ostream &print(std::ostream&, const String&); friend std::ostream &operator<<(ostream &os,const String &); public: String() = default; // cp points to a null terminated array, // allocate new memory & copy the array String(const char *cp) : sz(std::strlen(cp)), p(a.allocate(sz)) { std::uninitialized_copy(cp, cp + sz, p); } // copy constructor: allocate a new copy of the characters in s String(const String &s):sz(s.sz), p(a.allocate(s.sz)) { std::uninitialized_copy(s.p, s.p + sz , p); } // move constructor: copy the pointer, not the characters, // no memory allocation or deallocation String(String &&s) noexcept : sz(s.size()), p(s.p) { s.p = 0; s.sz = 0; } String(size_t n, char c) : sz(n), p(a.allocate(n)) { std::uninitialized_fill_n(p, sz, c); } // allocates a new copy of the data in the right-hand operand; // deletes the memory used by the left-hand operand String &operator=(const String &); // moves pointers from right- to left-hand operand String &operator=(String &&) noexcept; // unconditionally delete the memory because each String has its own memory ~String() noexcept { if (p) a.deallocate(p, sz); } // additional assignment operators String &operator=(const char*); // car = "Studebaker" String &operator=(char); // model = 'T' String & operator=(std::initializer_list ); // car = {'a', '4'} const char *begin() { return p; } const char *begin() const { return p; } const char *end() { return p + sz; } const char *end() const { return p + sz; } size_t size() const { return sz; } void swap(String &s) { auto tmp = p; p = s.p; s.p = tmp; auto cnt = sz; sz = s.sz; s.sz = cnt; } private: std::size_t sz = 0; char *p = nullptr; static std::allocator a; }; String make_plural(size_t ctr, const String &, const String &); inline void swap(String &s1, String &s2) { s1.swap(s2); } #endif #includeusing std::strlen; #include using std::copy; #include using std::size_t; #include using std::ostream; #include using std::swap; #include using std::initializer_list; #include using std::uninitialized_copy; #include "string.h" // define the static allocator member std::allocator String::a; // copy-assignment operator String & String::operator=(const String &rhs) { // copying the right-hand operand before deleting the left handles self-assignment auto newp = a.allocate(rhs.sz); // copy the underlying string from rhs uninitialized_copy(rhs.p, rhs.p + rhs.sz, newp); if (p) a.deallocate(p, sz); // free the memory used by the left-hand operand p = newp; // p now points to the newly allocated string sz = rhs.sz; // update the size return *this; } // move assignment operator String & String::operator=(String &&rhs) noexcept { // explicit check for self-assignment if (this != &rhs) { if (p) a.deallocate(p, sz); // do the work of the destructor p = rhs.p; // take over the old memory sz = rhs.sz; rhs.p = 0; // deleting rhs.p is safe rhs.sz = 0; } return *this; } String& String::operator=(const char *cp) { if (p) a.deallocate(p, sz); p = a.allocate(sz = strlen(cp)); uninitialized_copy(cp, cp + sz, p); return *this; } String& String::operator=(char c) { if(p) a.deallocate(p, sz); p = a.allocate(sz = 1); *p = c; return *this; } String& String::operator=(initializer_list il) { // no need to check for self-assignment if (p) a.deallocate(p, sz); // do the work of the destructor p = a.allocate(sz = il.size()); // do the work of the copy constructor uninitialized_copy(il.begin(), il.end(), p); return *this; } // named functions for operators ostream &print(ostream &os, const String &s) { auto p = s.begin(); while (p != s.end()) os << *p++ ; return os; } String add(const String &lhs, const String &rhs) { String ret; ret.sz = rhs.size() + lhs.size(); // size of the combined String ret.p = String::a.allocate(ret.sz); // allocate new space uninitialized_copy(lhs.begin(), lhs.end(), ret.p); // copy the operands uninitialized_copy(rhs.begin(), rhs.end(), ret.p + lhs.sz); return ret; // return a copy of the newly created String } // return plural version of word if ctr isn't 1 String make_plural(size_t ctr, const String &word, const String &ending) { return (ctr != 1) ? add(word, ending) : word; } // chapter 14 will explain overloaded operators ostream & operator<<(ostream &os,const String &s) { for(auto ptr = s.begin(); ptr != s.end(); ++ptr) os << *ptr << std::endl; //or can be written: print(os,s) return os; } String operator+(const String &lhs, const String &rhs) { return add(lhs, rhs); } int main() { return 0; } 14.8 同练习14.5
14.2.2重载输入运算符
istream & operator>>(istream &is,Sales_data &s) { double price; is >> s.bookNo >> s.units_sold >> price; if(is) s.revenue = price * s.units_sold; else s = Sales_data(); //input failure,object is set to the default state return is; }输入运算符定义注意事项如下: 1.输入后要判断流是否正确,如果正确继续执行往后的操作 2.如果错误,那么把被输入的对象置为默认状态。
习题14.2.2
习题14.9
istream & operator>>(istream &is,Sales_data &s) { double price; is >> s.bookNo >> s.units_sold >> price; if(is) s.revenue = price * s.units_sold; else s = Sales_data(); //input failure,object is set to the default state return is; } ostream & operator<<(ostream &os,const Sales_data &s) { os << s.bookNo << s.units_sold << s.revenue; return os; }提示:输入运算符必须在输入后立刻判断流的状态,如果流失败(说明读取数据发生错误),那么立刻把被输入的对象置于默认的状态。
14.10
(a)参数中传入的Sales_data 对象将会得到输入的值,其中BookNo、units_sold、price的值分别是:0-201-99999-9、10、24.95,同时,revenue的值是249.5。
(b)输入错误,参数中传入的Sales_data对象会得到默认值。
练习14.11
对于上题中的(a)程序将会正常执行,对于(b)程序将会发生错误,bookNo得到值10,units_sold得到值24,price得到值0.95,最后revenue得到值revenue = 24 * 0.95。
练习14.12
#include "date.h" #include#include using namespace std; istream & operator>>(istream &,Date &); ostream & operator<<(ostream &,const Date &); int main() { Date d; cin >> d; cout << d << endl; return 0; } #ifndef DATE_H_INCLUDE #define DATE_H_INCLUDE #include#include using namespace std; class Date { public: Date(){} Date(int y,int m,int d){year = y; month = m; day = d;} friend istream & operator>>(istream &is,Date &dt) { is >> dt.year >> dt.month >> dt.day; if(!is) { dt = Date(0,0,0); } return is; } friend ostream & operator<<(ostream &os,const Date& dt) { os << dt.year << dt.month << dt.day; return os; } private: int year,month,day; }; #endif 习题14.17
bool operator==(const Date &lhs,const Date &rhs) { return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day; } bool operator!=(const Date &lhs,const Date &rhs) { return !(lhs == rhs); }判断几个条件是否同时成立,可以使用 && 把这几个条件连起来。秒。
14.3.2节
练习14.18
对于Sales_data的==运算符来说,如果两笔交易的revenue和units_sold成员不同,那么即时他们的ISBN相同也无济于事,他们仍然是不相同的。如果我们定义的<运算符仅仅比较ISBN成员,那么将发生这样的情况:两个ISBN相同但是revenue和units_sold不同的对象经比较是不想等的,但是其中的任何一个都不小于另一个。然而实际情况是,如果我们有两个对象并且一个都不比另一个小,则从道理上来说这两个对象应该是相等的。



