14.1
不同点:重载运算符至少有一个操作数是类类型,而且对于部分运算符它无法保留求值顺序或短路求值属性
相同点:对于一个重载运算符来说,其优先级和结合律与对应的内置运算符保持一致
14.2
1 #include <iostream> 2 #include <vector> 3 #include <string> 4 5 using namespace std; 6 7 class Sales_data { 8 friend istream &operator>>(istream &is, Sales_data&); 9 friend ostream &operator<<(ostream &os, Sales_data&); 10 public: 11 //构造函数 12 Sales_data() = default; 13 //重载运算符 14 Sales_data &operator+=(const Sales_data&); 15 private: 16 string bookNo; //书的ISBN 17 unsigned units_sold = 0; //售出的本数 18 double revenue = 0.0; //销售额 19 }; 20 21 istream &operator>>(istream &is, Sales_data &book) 22 { 23 is >> book.bookNo >> book.units_sold >> book.revenue; 24 return is; 25 } 26 27 ostream &operator<<(ostream &os, Sales_data &book) 28 { 29 os << book.bookNo << " " << book.units_sold << " " << book.revenue; 30 return os; 31 } 32 33 Sales_data &Sales_data::operator+=(const Sales_data &rhs) 34 { 35 units_sold += rhs.units_sold; 36 revenue += rhs.revenue; 37 return *this; 38 } 39 40 Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) 41 { 42 Sales_data total = lhs; 43 total += rhs; 44 return total; 45 } 46 47 48 int main() 49 { 50 Sales_data a; 51 cin >> a; 52 Sales_data b, c; 53 b = a; 54 c = a + b; 55 cout << a << endl << b << endl << c << endl; 56 return 0; 57 }
14.3
(a):使用了内置版本的==,比较两个指针
(b):使用了string重载的==
(c):使用了vector重载的==
(d):使用了string重载的==
14.4
应该是类的成员:%=、++、->、()
14.9
istream &operator>>(istream &is, Sales_data &book) { is >> book.bookNo >> book.units_sold >> book.revenue; if (!is) book = Sales_data(); return is; }
14.10
(a):正确
(b):由于const char*不能转换为double,所以读取操作失败,对象将被赋予默认的状态
14.11
存在错误,这个定义函数没有实现对输入数据正确性的判断
如果给定上个练习的(b),则revenue的值将是未定义的
14.13
可以定义一个减法运算符
Sales_data &Sales_data::operator-=(const Sales_data &rhs) { units_sold -= rhs.units_sold; revenue -= rhs.revenue; return *this; } Sales_data operator-(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sub = lhs; sub -= rhs; return sub; }
14.14
使用operator+=来定义operator+比较好,可以省去重复完成operator+中做的事
14.20
Sales_data &Sales_data::operator+=(const Sales_data &rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; sum += rhs; return sum; }
14.21
Sales_data &Sales_data::operator+=(const Sales_data &rhs) { units_sold = units_sold + rhs.units_sold; revenue = revenue + rhs.revenue; return *this; } Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs) { Sales_data sum = lhs; sum.units_sold += rhs.units_sold; sum.revenue += rhs.revenue; return sum; }
缺点:可读性较差
14.22
Sales_data &Sales_data::operator=(const string &s) { bookNo = s; return *this; }
14.23
StrVec &StrVec::operator=(initializer_list<string> il) { auto data = alloc_n_copy(il.begin(), il.end()); free(); elements = data.first; first_free = cap = data.second; return *this; }
14.26
class StrVec { public: string &operator[](size_t n) { return elements[n]; } const string &operator[](size_t n) const { return elements[n]; } };
14.27
class StrBlobPtr { public: //前置运算符 StrBlobPtr &operator++(); StrBlobPtr &operator--(); //后置运算符 StrBlobPtr &operator++(int); StrBlobPtr &operator--(int); }; StrBlobPtr &StrBlobPtr::operator++() { check(curr, " increment past end of StrBlobPtr "); ++curr; return *this; } StrBlobPtr &StrBlobPtr::operator--() { --curr; check(-1, " decrement past begin of StrBlobPtr "); return *this; } StrBlobPtr StrBlobPtr::operator++(int) { StrBlobPtr ret = *this; ++*this; return ret; } StrBlobPtr StrBlobPtr::operator--(int) { StrBlobPtr ret = *this; --*this; return ret; }
14.29
对于++和--运算符,无论是前缀版本还是后缀版本,都会改变调用对象本身的值,因此是不能定义成const的。
14.31
对于StrBlobPtr类,它的数据成员有两个,分别是weak_ptr<vector<string>>和size_t类型的,前者定义了自己的拷贝构造函数、赋值运算符和析构函数,后者是内置类型。所以该类不需要定义析构函数,从而相应的拷贝构造函数、赋值运算符也不需要定义,使用相应的合成版本即可。
14.33
与相应的函数调用运算符的形参数目一致
14.34
#include <iostream> using namespace std; class A { public: string operator()(size_t i, string s1, string s2) const { return i % 2 ? s1 : s2; } }; int main() { A a; string s = a(1, "odd", "even"); cout << s << endl; return 0; }
14.35
#include <iostream> #include <vector> #include <string> using namespace std; class A { public: A(istream &in = cin): is(in) {} string operator()() { string line; getline(is, line); if (!is) line = ""; return line; } private: istream &is; }; int main() { A a; cout << a() << endl; return 0; }
14.36
int main() { A a; vector<string> vec; string s = a(); while (s != "") { vec.push_back(s); s = a(); } for (auto &i : vec) cout << i << endl; return 0; }
14.37
#include <iostream> #include <vector> #include <string> #include <list> #include <algorithm> using namespace std; class A { public: A() = default; A(int i): val(i) {} bool operator()(const int &a) { return val == a; } private: int val; }; int main() { list<int> lst = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; //将lst中值为2的元素替换为22 replace_if(lst.begin(), lst.end(), A(2), 22); return 0; }
14.38
#include <iostream> #include <fstream> #include <vector> #include <string> #include <list> #include <algorithm> using namespace std; class A { public: int operator()(const string &s) { return s.size(); } }; int main() { ifstream in("data.txt"); int arr[30] = {0}; A a; string word; while (in >> word) { int pos = a(word); ++arr[pos]; } for (int i = 1; i != 11; ++i) cout << "长度为" << i << "的单词的个数是:" << arr[i] << endl; return 0; }
14.39
#include <iostream> #include <fstream> #include <vector> #include <string> #include <list> #include <algorithm> using namespace std; class A { public: bool operator()(const string &s) { if (s.size() < 10) return true; return false; } }; int main() { ifstream in("data.txt"); int num1 = 0, num2 = num1; A a; string word; while (in >> word) { bool flag = a(word); if (flag) ++num1; else ++num2; } cout << "长度小于10的单词的个数是:" << num1 << endl; cout << "长度大于10的单词的个数是:" << num2 << endl; return 0; }
14.40
class A { public: bool operator()(const string &s1, const string &s2) { return s1.size() < s2.size(); } }; class B { public: A(int i): sz(i) {} bool operator()(const string &s) { return s.size() >= sz; } private: int sz; }; class C { public: void operator()(const string &s) { cout << s << " "; } }; void biggies(vector<string> &words, vector<string>::size_type sz) { stable_sort(words.begin(), words.end(), A()); auto wc = find_if(words.begin(), words.end(), B(sz)); for_each(wc, words.end(), C()); }
14.41
在C++11中,lambda是通过匿名的函数对象来实现的,因此我们可以把lambda看作是对函数对象在使用方式上进行的简化。当代码需要一个简单的函数,并且这个函数并不会在其他地方被使用时,就可以使用lambda来实现,此时它所起的作用类似于匿名函数。但如果这个函数需要多次使用,并且它需要保存某些状态的话,使用函数对象更合适一些。
即,lambda表达式方便创建、使用,不需要额外的为调用对象声明类;在一次性需要使用函数对象时使用lambda,在多次需要使用到相同的函数对象时使用类。
14.42
vector<int> ivec{520, 1020, 1030}; vector<string> svec{"pooh", "pooh", "pool", "pooh", "poow"}; auto num = count_if(ivec.begin(), ivec.end(), bind(greater<int>(), _1, 1024)); auto it = find_if(svec.begin(), svec.end(), bind(not_equal_to<string>(), _1, "pooh")); transform(ivec.begin(), ivec.end(), ivec.begin(), bind(multiplies<int>(), _1, 2));
14.43
int main() { vector<int> ivec{520, 1020, 1030}; int div; cin >> div; //返回取余结果为0的数的个数 int num = count_if(ivec.begin(), ivec.end(), bind(modulus<int>(), _1, div)); if (!num) cout << "全部可整除" << endl; else cout << "不能全部被整除" << endl; return 0; }
14.44
#include <iostream> #include <map> #include <functional> using namespace std; map<string, function<int (int, int)>> binops; class multiply { public: int operator()(int a, int b) const { return a * b; } }; int add(int a, int b) { return a + b; } auto divide = [](int a, int b) { return a / b; }; void func() { binops.insert({"+", add}); binops.insert(make_pair("-", minus<int>())); binops.insert(pair<string, function<int (int, int)>>("*", multiply())); binops.insert(map<string, function<int (int, int)>>::value_type("/", divide)); binops.insert({"%", [](int a, int b) { return a % b; }}); } int main() { func(); cout << "请按照此形式(如:a + b)输入计算表达式: "; int a, b; string s; while (cin >> a >> s >> b) { cout << binops[s](a, b) << endl; cout << "请按照此形式(如:a + b)输入计算表达式: "; } return 0; }
14.45
operator string() const { return bookNo; } operator double() const { return revenue; }
14.46
Sales_data不应该定义这两种类型转换运算符,因为对于类来说,它包含三个数据成员:bookNo,units_sold和revenue,只有三者在一起才是有效的数据。但是如果确实想要定义这两个类型转换运算符的话,应该把它们声明成explicit的,这样可以防止sales_data 在默写情况下被默认转换成string或double类型,这有可能导致意料之外的运算结果。
14.47
operator const int();:将对象转换成const int,没有意义,因为只有在接受const int值的地方才能够使用
operator int() const:将对象转换成int值,const 表示转换时不会修改对象数据
14.50
对于int ex1 = ldObj;,它需要把LongDouble类型转换成int类型,但是LongDouble并没有定义对应的类型转换运算符,因此它会尝试使用其他的来进行转换。题中给出的两个都满足需求,但编译器无法确定那一个更合适,因此会产生二义性错误。
对于float ex2 = ldObj;,它需要把LongDouble转换成float类型,而我们恰好定义了对应的类型转换运算符,因此直接调用operator float()即可。
14.51
这里会优先调用void calc(int);
因为double转换为int是标准类型转换,而转换为LongDouble则是转换为用户自定义类型,实际上调用了转换构造函数,因此前者优先。
14.52
ans1:
对于ld=si+ld,由于LongDouble不能转换为SmallInt,因此Smallint的成员operator+和friend operator都不可行。
由于Smallint不能转换为LongDouble,LongDouble的成员operator+和非成员operator+也都不可行。
由于SmallInt可以转换为int, LongDouble了可以转换为float和double,所以内置的operator+(int, float)和operator+(int, double)都可行,会产生二义性。
对于ld=ld+si,类似上一个加法表达式,由于Smallint不能转换为double,LongDouble也不能转换为SmallInt,因此SmallInt的成员operator+和两个非成员operator+都不匹配。
LongDouble的成员operator+可行,且为精确匹配。
SmallInt可以转换为int,longDouble可以转换为float和double,因此内置的operator+(float, int)和operator(double, int)都可行。但它们都需要类型转换,因此LongDouble的成员operator+优先匹配。
ans2:
ld=si+ld;二义性,si将转换为int,但ld可以转换为double也可以使float
ld=ld+si;精确匹配LongDouble operator+(const SmallInt &);,虽然使用后面的也可以,但是需要转换,所以前者更好
14.53
内置的operator+(int, double)是可行的,而3.14可以转换为int,然后再转换为SmallInt,所以SmallInt的成员operator+也是可行的。两者都需要进行类型转换,所以会产生二义性。改为:double d = s1 +Smallint(3.14);即可。