练习14.1
区别:
1.可以直接调用重载的运算符
2.至少有一个运算对象是类类型
3.无法保留运算对象的求值顺序规则和短路求值规则
共同点:
重载运算符的优先级和结合律与对应的内置运算符保持一致
练习14.2
#ifndef SALES_DATA_H #define SALES_DATA_H #include <string> #include <ostream> using std::string; using std::istream; using std::ostream; using std::endl; class Sales_data { friend istream& operator>>(istream& is, Sales_data& it); friend ostream& operator<<(ostream& os, Sales_data& it); private: string bookNo; unsigned units_sold = { 0 }; double revenue = { 0.0 }; public: const string& isbn() const { return bookNo; } Sales_data& operator+=(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } Sales_data(string bn, unsigned us = 0, double re = 0.0) :bookNo(bn), units_sold(us), revenue(re) { std::cout << "Sales_data(string bn, unsigned us = 0, double re = 0.0)" << std::endl;} Sales_data() :Sales_data("") { std::cout << "Sales_data()" << std::endl; } Sales_data(istream& is) :Sales_data() { is>> *this; std::cout << "Sales_data(istream& is)" << std::endl; } }; Sales_data operator+(Sales_data& lhs, Sales_data& rhs) { Sales_data sum = lhs; sum += rhs; return sum; } istream & operator>>(istream& is, Sales_data& it) { is >> it.bookNo >> it.units_sold >> it.revenue; return is; } ostream& operator<<(ostream& os, Sales_data& it) { os << it.isbn() << " " << it.units_sold << " " << it.revenue << endl; return os; } #endif // !SALES_DATA_H
练习14.3
"cobble" == "stone"; //两者元素中既不是string也不是vector,因此两个版本都不是 svec1[0] == svec2[0]; //string svec1 == svec2; //vector svec1[0] == "stone"; //string
练习14.4
(a) % 算数运算符,通常为普通的非成员函数
(b) %= 复合赋值运算符,通常为成员函数
(c) ++ 改变对象状态的运算符,通常为成员函数
(d) -> 必须是成员函数
(e) << 通常为普通的非成员函数
(f) && 通常为普通的非成员函数
(g) == 通常为普通的非成员函数
(h) () 必须是成员函数
练习14.5
#ifndef DATE_H_ #define DATE_H_ #include<string> using std::string; class Date { public: Date(string&); ~Date(); unsigned getYear()const { return year; } unsigned getMonth() const { return month; } unsigned getDay() const { return day; } private: unsigned year; unsigned month; unsigned day; void str_to_month(string &str,string::size_type& pos); }; Date::Date(string &s) { string::size_type pos1, pos2; if (pos1 = s.find_first_of(",/") == string::npos) { pos1 = s.find_first_of(" "); pos2 = s.find_last_of(" "); str_to_month(s, pos1); day = stoul(s.substr(pos1 + 1, pos2)); year = stoul(s.substr(pos2 + 1, s.size())); } else if (pos1 = s.find_first_of(",") == string::npos) { pos1 = s.find_first_of("/"); pos2 = s.find_last_of("/"); str_to_month(s, pos1); day = stoul(s.substr(pos1 + 1, pos2)); year = stoul(s.substr(pos2 + 1, s.size())); } else if (pos1 = s.find_first_of("/") == string::npos) { pos1 = s.find_first_of(" "); pos2 = s.find_last_of(","); str_to_month(s, pos1); day = stoul(s.substr(pos1 + 1, pos2)); year = stoul(s.substr(pos2 + 1, s.size())); } else { day = month = year = 0; } } Date::~Date() { } inline void Date::str_to_month(string& s, string::size_type& pos1) { if (s.substr(0, pos1) == "Jan" || s.substr(0, pos1) == "January") { month = 1; } else if (s.substr(0, pos1) == "Feb" || s.substr(0, pos1) == "February") { month = 2; } else if (s.substr(0, pos1) == "Mar" || s.substr(0, pos1) == "March") { month = 3; } else if (s.substr(0, pos1) == "Apr" || s.substr(0, pos1) == "April") { month = 4; } else if (s.substr(0, pos1) == "May" || s.substr(0, pos1) == "May") { month = 5; } else if (s.substr(0, pos1) == "Jun" || s.substr(0, pos1) == "June") { month = 6; } else if (s.substr(0, pos1) == "Jul" || s.substr(0, pos1) == "July") { month = 7; } else if (s.substr(0, pos1) == "Aug" || s.substr(0, pos1) == "August") { month = 8; } else if (s.substr(0, pos1) == "Sept" || s.substr(0, pos1) == "September") { month = 9; } else if (s.substr(0, pos1) == "Oct" || s.substr(0, pos1) == "October") { month = 10; } else if (s.substr(0, pos1) == "Nov" || s.substr(0, pos1) == "November") { month = 11; } else if (s.substr(0, pos1) == "Dec" || s.substr(0, pos1) == "December") { month = 12; } else { month = stoul(s.substr(0, pos1)); } } std::ostream& operator<< (std::ostream& os,const Date& d) { os << d.getYear() << "-" << d.getMonth() << "-" << d.getDay(); return os; } #endif // !DATE_H_
练习14.6
ostream& operator<<(ostream& os, const Sales_data& it) { os << it.isbn() << " " << it.units_sold << " " << it.revenue; return os; }
练习14.7
ostream& operator<<(ostream& os, const String& s) { auto sl = s.begin(); while (sl != s.end()) os << *(sl++); return os; }
练习14.8
std::ostream& operator<< (std::ostream& os,const Date& d) { os << d.getYear() << "-" << d.getMonth() << "-" << d.getDay(); return os; }
练习14.9
istream & operator>>(istream& is, Sales_data& it) { is >> it.bookNo >> it.units_sold >> it.revenue; if (!is) it = Sales_data(); return is; }
练习14.10
(a)
0-201-99999-9 10 24.95
0-201-99999-9 10 24.95
(b)
10 24.95 0-210-99999-9
10 24 0.95
练习14.11
没有输入检查,什么也不会发生
练习14.12
std::istream& operator>> (std::istream& is, Date& d) { is >> d.year >> d.month >> d.day; if (!is) { std::string s = ""; d = Date(s); } return is; }
练习14.13
还应该支持operator-运算符,因此也应该支持operator-=复合运算符
Sales_data& operator-=(const Sales_data& rhs) { units_sold -= rhs.units_sold; revenue -= rhs.revenue; return *this; } Sales_data operator-(Sales_data& lhs, Sales_data& rhs) { Sales_data sum = lhs; sum -= rhs; return sum; }
练习14.14
因为operator+=不需要创建临时空间,所以调用operator+=比其它方法更奏效
练习14.15
不是,因为Date类对于类对象的+-计算会产生二义性
练习14.16
StrBlob类,StrBlobPtr类
inline bool operator==(const StrBlob& lhs, const StrBlob& rhs) { return *lhs.data == *rhs.data; } inline bool operator!=(const StrBlob& lhs, const StrBlob& rhs) { return !(lhs==rhs); } // named equality operators for StrBlobPtr inline bool operator==(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { auto l = lhs.wptr.lock(), r = rhs.wptr.lock(); // if the underlying vector is the same if (l == r) // then they're equal if they're both null or // if they point to the same element return (!r || lhs.curr == rhs.curr); else return false; // if they point to difference vectors, they're not equal } inline bool operator!=(const StrBlobPtr &lhs, const StrBlobPtr &rhs) { return !(lhs==rhs); } // named equality operators for StrBlobPtr inline bool operator==(const ConstStrBlobPtr& lhs, const ConstStrBlobPtr& rhs) { auto l = lhs.wptr.lock(), r = rhs.wptr.lock(); // if the underlying vector is the same if (l == r) // then they're equal if they're both null or // if they point to the same element return (!r || lhs.curr == rhs.curr); else return false; // if they point to difference vectors, they're not equal } inline bool operator!=(const ConstStrBlobPtr& lhs, const ConstStrBlobPtr& rhs) { return !(lhs==rhs); }
StrVec类
bool operator==(const StrVec& lhs, const StrVec& rhs) { if (lhs.size() != rhs.size()) { return false; } else { for (auto l_iter = lhs.begin(),r_iter = rhs.begin(); l_iter != lhs.end(); ++l_iter, ++r_iter) { if (*l_iter != *r_iter) { return false; } } } return true; } inline bool operator!=(const StrVec& lhs, const StrVec& rhs) { return !(lhs==rhs); }
String类
inline bool operator==(const String& lhs, const String& rhs) { if (lhs.size() != rhs.size()) { return false; } else { for (auto l_iter = lhs.begin(), r_iter = rhs.begin(); l_iter != lhs.end(); ++l_iter, ++r_iter) { if (*l_iter != *r_iter) { return false; } } } return true; } inline bool operator!=(const String& lhs, const String& rhs) { return !(lhs == rhs); }
练习14.19
inline bool operator==(const Date& lhs, const Date& rhs) { return lhs.year==rhs.year&& lhs.month == rhs.month && lhs.day == rhs.day; } inline bool operator!=(const Date& lhs, const Date& rhs) { return !(lhs == rhs); }
练习14.20
Sales_data& operator+=(const Sales_data& rhs) { units_sold += rhs.units_sold; revenue += rhs.revenue; return *this; } Sales_data operator+(Sales_data& lhs, Sales_data& rhs) { Sales_data sum = lhs; sum += rhs; return sum; }
练习14.21
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; } Sales_data& operator+=(const Sales_data& rhs) { const Sales_data tmp = *this; *this = tmp + rhs; return *this; }
operator+=运算符调用operator+运算符前需要创建临时内存空间
练习14.22
Sales_data& Sales_data::operator=(string& s) { bookNo = s; units_sold = 0; revenue = 0; }
练习14.23
inline StrVec& StrVec::operator=(std::initializer_list<std::string> il) { auto newdata = alloc_n_copy(il.begin(), il.end()); free(); elements = newdata.first; first_free = cap = newdata.second; return *this; }
练习14.24
Date类不需要实现拷贝赋值和移动赋值运算符,因为Date类中只含有三个int变量,默认的浅拷贝就能满足要求。
inline Date& Date::operator=(const Date& d) { year = d.year; month = d.month; day = d.day; } inline Date& Date::operator=(Date&&d) { year = d.year; month = d.month; day = d.day; }
练习14.25
还应该定义赋值运算符,参数为string,将date作为一个字符串赋值给Date类。
inline Date& Date::operator=(string&s) { string::size_type pos1, pos2; if (pos1 = s.find_first_of(",/") == string::npos) { pos1 = s.find_first_of(" "); pos2 = s.find_last_of(" "); str_to_month(s, pos1); day = stoul(s.substr(pos1 + 1, pos2)); year = stoul(s.substr(pos2 + 1, s.size())); } else if (pos1 = s.find_first_of(",") == string::npos) { pos1 = s.find_first_of("/"); pos2 = s.find_last_of("/"); str_to_month(s, pos1); day = stoul(s.substr(pos1 + 1, pos2)); year = stoul(s.substr(pos2 + 1, s.size())); } else if (pos1 = s.find_first_of("/") == string::npos) { pos1 = s.find_first_of(" "); pos2 = s.find_last_of(","); str_to_month(s, pos1); day = stoul(s.substr(pos1 + 1, pos2)); year = stoul(s.substr(pos2 + 1, s.size())); } else { day = month = year = 0; } }
练习14.26
StrBlob类
inline std::string& StrBlob::operator[](size_t n) { check(n, "out of range"); return data->at(n); } inline const std::string& StrBlob::operator[](size_t n) const { check(n, "out of range"); return data->at(n); }
StrBlobPtr类
inline std::string& StrBlobPtr::operator[](size_t n) { auto ret=check(n, "out of range"); return (*ret)[n]; } inline const std::string& StrBlobPtr::operator[](size_t n) const { auto ret = check(n, "out of range"); return (*ret)[n]; }
StrVec类
inline std::string& StrVec::operator[](size_t n) { if (size() > n && n >= 0)return *(elements + n); } inline const std::string& StrVec::operator[](size_t n) const { if (size() > n && n >= 0)return *(elements + n); }
String类
char& operator[](size_t n) { return elements[n]; }; const char& operator[](size_t n)const { return elements[n]; };
练习14.27
inline StrBlobPtr& StrBlobPtr::operator++() { check(curr, "increment past out end of StrBlobPtr"); ++curr; return *this; } inline StrBlobPtr StrBlobPtr::operator++(int) { StrBlobPtr ret = *this; ++*this; return ret; } inline StrBlobPtr& StrBlobPtr::operator--() { --curr; check(curr, "increment past begin end of StrBlobPtr"); return *this; } inline StrBlobPtr StrBlobPtr::operator--(int) { StrBlobPtr ret = *this; --* this; return ret; }
练习14.28
inline StrBlobPtr& StrBlobPtr::operator+=(size_t n) { curr += n; check(curr, "increment past out end of StrBlobPtr"); return *this; } inline StrBlobPtr StrBlobPtr::operator+(size_t n) const { StrBlobPtr ret = *this; ret += n; return ret; } inline StrBlobPtr& StrBlobPtr::operator-=(size_t n) { curr -= n; check(curr, "increment past begin end of StrBlobPtr"); return *this; } inline StrBlobPtr StrBlobPtr::operator-(size_t n) const { StrBlobPtr ret = *this; ret -= n; return ret; }
练习14.29
递增和递增需要改变对象元素,因此不定义const版本
练习14.30
inline std::string& StrBlobPtr::operator*() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } inline std::string* StrBlobPtr::operator->() const { return &this->operator*(); } inline const std::string& ConstStrBlobPtr::operator*() const { auto p = check(curr, "dereference past end"); return (*p)[curr]; } inline const std::string* ConstStrBlobPtr::operator->() const { return &this->operator*(); }
练习14.31
StrBlobPtr没有动态分配的内存,因此不需要自定义的析构函数,又由三五法则可知不需要定义拷贝构造函数和赋值运算符
练习14.32
class StrBlobPtr_pointer { public: StrBlobPtr_pointer() = default; StrBlobPtr_pointer(StrBlobPtr* p) : pointer(p) { } StrBlobPtr& operator *() const; StrBlobPtr* operator->() const; private: StrBlobPtr* pointer = nullptr; }; StrBlobPtr& StrBlobPtr_pointer::operator *() const { return *(this->pointer); } StrBlobPtr* StrBlobPtr_pointer::operator ->() const { return &this->operator*(); } int main(int argc,char*argv[]) { StrBlob sb{ "hello", "world" }; StrBlobPtr iter = sb.begin(); StrBlobPtr_pointer p(&iter); std::cout << p->deref() << std::endl; }
练习14.33
重载的函数调用运算符和该运算符能接受的运算对象相同,因此最大值为256
练习14.34
struct compInt { const int operator()(const int val1, const int val2, const int val3)const { return (val1 ? val2 : val3); } }; int main(int argc,char*argv[]) { compInt c; std::cout << c(1,2,3) << std::endl; }
练习14.35
class ScanfString { public: ScanfString(istream& i ) :is(i){ getline(i, str); } string operator()() { return str; } private: istream& is; string str; }; int main(int argc,char*argv[]) { ScanfString c(cin); std::cout << c() << std::endl; }
练习14.36
class ScanfString { public: ScanfString(istream& i ) :is(i){ string s; while (i) { getline(i, s); str.emplace_back(s); } } void operator()() { for_each(str.begin(), str.end(), [&](string& s) {cout << s << endl; }); } private: istream& is; vector<string> str; }; int main(int argc,char*argv[]) { ScanfString c(cin); c(); }
练习14.37
class CompInt { public: CompInt(int i ) :comp(i){} bool operator()(int i) { return comp == i; } private: int comp; }; int main(int argc,char*argv[]) { vector<int> vi{ 0,1,2,3,4,3,4,5 }; CompInt ci(3); replace_if(vi.begin(), vi.end(), ci,10); for_each(vi.begin(), vi.end(), [](int& i) {cout << i << " "; }); }
练习14.38
class CompInt { public: CompInt(size_t l, size_t u) :lower(l), upper(u) {} CompInt(size_t c = 0) :lower(c), upper(c) {} bool operator()(string &s) { return(s.length() >= lower && s.length() <= upper); } private: size_t lower; size_t upper; }; int main(int argc,char*argv[]) { map<size_t, size_t> words_len; for (size_t i = 1; i < 11; ++i) { ifstream ifs(argv[1]); CompInt ci(i); for (std::string word; ifs >> word;) { if (ci(word)) ++words_len[i]; } } for (auto i : words_len) { cout << i.first << ":" << i.second << endl; } }
练习14.39
class CompInt { public: CompInt(size_t l, size_t u) :lower(l), upper(u) {} CompInt(size_t c = 0) :lower(c), upper(c) {} bool operator()(string &s) { return(s.length() >= lower && s.length() <= upper); } private: size_t lower; size_t upper; }; int main(int argc,char*argv[]) { map<size_t, size_t> words_len; ifstream ifs(argv[1]); CompInt ci1(1,9); CompInt ci2(10,SIZE_MAX); for (std::string word; ifs >> word;) { if (ci1(word)) ++words_len[1]; else if (ci2(word)) ++words_len[10]; } for (auto i : words_len) { cout << i.first << ":" << i.second << endl; } }
练习14.40
class compString { public: compString(size_t s):sz(s){} const bool operator()(const string& str1, const string& str2)const { return (str1.size() >= str2.size()); } const bool operator()(const string& str)const { return str.size() > sz; } private: size_t sz; }; class printStr { public: void operator()(const string& str)const { cout << str << " "; } private: }; void elimDups(vector<string>& words) { sort(words.begin(), words.end()); auto end_unique = unique(words.begin(), words.end()); words.erase(end_unique, words.end()); } void biggies(vector<string>& words, size_t sz) { elimDups(words); compString cs(sz); printStr ps; stable_sort(words.begin(), words.end(), cs); auto wc = stable_partition(words.begin(), words.end(), cs); for_each(words.begin(), wc, ps); }
练习14.41
在某些时候, 使用lamdba更方便。当函数方法不经常使用或不复杂时, 可以使用 lambda, 而当调用函数方法实现较复杂且要被频繁调用则使用类。
练习14.42
int main(int argc, char* argv[]) { vector<int> iv; for (auto i = 1000; i < 2000; i+=2) iv.emplace_back(i); cout<<"大于1024的数字有"<<count_if(iv.begin(), iv.end(), bind(std::greater<int>(),_1, 1024))<<"个"<<endl; vector<string> svec = { "pooh", "apple", "banana", "pooh" }; auto word = find_if(svec.cbegin(), svec.cend(), bind(std::not_equal_to<string>(), _1, "pooh")); cout << *word << endl; transform(iv.begin(), iv.end(), iv.begin(), bind(std::multiplies<int>(), _1, 2)); for (auto i : iv) { cout << i << " "; } }
练习14.43
int main(int argc, char* argv[]) { vector<int> iv{1,2,4,8,16}; if (any_of(iv.begin(), iv.end(), bind(std::modulus<int>(), 1024, _1))) { cout << "给定int值不能被int容器中的所有元素整除" << endl; } else { cout << "给定int值能被int容器中的所有元素整除" << endl; } }
练习14.44
int main(int argc, char* argv[]) { std::map<std::string, std::function<int(int, int)>> binops = { {"+", std::plus<int>()}, {"-", std::minus<int>()}, {"/", std::divides<int>()}, {"*", std::multiplies<int>()}, {"%", std::modulus<int>()} }; while (true) { int n1, n2; std::string s; std::cin >> n1 >> s >> n2; std::cout << binops[s](n1, n2) << endl; } return 0; }
练习14.45
explicit operator string() const { return bookNo; } explicit operator double() const { return revenue; }
int main(int argc, char* argv[]) { Sales_data s; cout << static_cast<string>(s) << endl; cout << static_cast<double>(s) << endl; return 0; }
练习14.46
不应该定义上述两种类型转换运算符,因为对于使用者来说理解难度高,应该尽量避免让使用者去进行强制类型转换。应该声明为explicit,避免隐式的强制类型转换
练习14.47
struct Integal { operator const int();//返回的类型是const类型 operator int() const;//不允许对象在函数内被修改 };
练习14.48
应该含有,因为Date类需要进行格式判断,又因为向bool的类型转换通常用在条件部分,因此一般定义成explicit的
练习14.49
class Date { friend std::istream& operator>> (std::istream& , Date& ); friend bool operator==(const Date& lhs, const Date& rhs); friend bool operator!=(const Date& lhs, const Date& rhs); public: Date():day(0),month(0),year(0) {} Date(string&); Date& operator=(const Date&); Date& operator=(Date&&)noexcept; Date& operator=(string&); ~Date(); unsigned getYear()const { return year; } unsigned getMonth() const { return month; } unsigned getDay() const { return day; } explicit operator bool() const { return 1 <= month && month <= 12 && 1 <= day && day <= month_days[((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) ? 1 : 0][month - 1]; } private: unsigned year; unsigned month; unsigned day; static vector<vector<int>> month_days; void str_to_month(string &str,string::size_type& pos); }; vector<vector<int>> Date::month_days{ { 31,28,31,30,31,30,31,31,30,31,30,31 } ,{ 31,29,31,30,31,30,31,31,30,31,30,31 } };
练习14.50
struct LongDouble { LongDouble (double = 0.0); operator double(); operator float(); }; LongDouble ldObj; int ex1 = ldObj; // 在将ldObj转换为int时,类定义的类型转换都无法精准匹配,因此会产生二义性,可能先执行 operator double(), 再进行double到int的转换。也可能调用operator float(),再进行float到int的转换。 float ex2 = ldObj; // 调用operator float()
练习14.51
会优先调用calc(int),因为doube转int是标准类型转换,而LongDouble转int是用户自定义转换。
练习14.52
struct LongDouble { LongDouble operator+ (const SmallInt&); }; LongDouble operator+(LongDouble&, double); SmallInt si; LongDouble ld; ld = si + ld;//没有与这些操作数匹配的+运算符,这是因为上面定义的两个操作符都不够精确 ld = ld + si;//C++ LongDouble LongDouble::operator+(const SmallInt &)
练习14.53
SmallInt s1; double d = s1 + 3.14;
不合法,因为s1可以隐式转换为int型,而内置operator+支持int和double的加法,或者将double类型3.14转换为int再转换为SmallInt进行operator+为两个SmallInt的加法,存在二义性
改为
int main(int argc, char* argv[]) { SmallInt s1; double d = s1 + SmallInt(3.14); return 0; }