14.1节

14.1答

不同点:

重载操作符必须具有至少一个class或枚举类型的操作数。

重载操作符不保证操作数的求值顺序,例如对&&和| | 的重载版本不再具有“短路求值”的特征,两个操作数,两个操作数都要进行求值,而且不规定操作数的求值顺序。

相同点:

对于优先级和结合性及操作数的数目都不变。

14.2 答:

文件sales_data.cc

#include <iostream>
#include <string>
#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 <iostream>
#include <string>
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 <iostream>
#include <string>
#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<<sep << "day:"<<dt.day;
return os;
}int main()
{Date d(2021,11,14);cout << d << endl;return    0;
}

14.2节

14.2.1习题

习题14.6

14.7

/** This file contains code from "C++ Primer, Fifth Edition", by Stanley B.* Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the* copyright and warranty notices given in that book:* * "Copyright (c) 2013 by Objectwrite, Inc., Josee Lajoie, and Barbara E. Moo."* * * "The authors and publisher have taken care in the preparation of this book,* but make no expressed or implied warranty of any kind and assume no* responsibility for errors or omissions. No liability is assumed for* incidental or consequential damages in connection with or arising out of the* use of the information or programs contained herein."* * Permission is granted for this code to be used for educational purposes in* association with the book, given proper citation if and when posted or* reproduced.Any commercial use of this code requires the explicit written* permission of the publisher, Addison-Wesley Professional, a division of* Pearson Education, Inc. Send your request for permission, stating clearly* what code you would like to use, and in what specific way, to the following* address: * *  Pearson Education, Inc.*    Rights and Permissions Department*  One Lake Street*    Upper Saddle River, NJ  07458*  Fax: (201) 236-3290
*/ #ifndef STRING_H
#define STRING_H#include <cstring>
#include <algorithm>
#include <cstddef>
#include <iostream>
#include <initializer_list>
#include <iostream>
#include <memory>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 arrayString(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 sString(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 deallocationString(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 operandString &operator=(const String &);// moves pointers from right- to left-hand operandString &operator=(String &&) noexcept;// unconditionally delete the memory because each String has its own memory~String() noexcept { if (p) a.deallocate(p, sz); }// additional assignment operatorsString &operator=(const char*);         // car = "Studebaker"String &operator=(char);                // model = 'T'String &operator=(std::initializer_list<char>); // 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<char> a;
};
String make_plural(size_t ctr, const String &, const String &);
inline
void swap(String &s1, String &s2)
{s1.swap(s2);
}#endif
/** This file contains code from "C++ Primer, Fifth Edition", by Stanley B.* Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the* copyright and warranty notices given in that book:* * "Copyright (c) 2013 by Objectwrite, Inc., Josee Lajoie, and Barbara E. Moo."* * * "The authors and publisher have taken care in the preparation of this book,* but make no expressed or implied warranty of any kind and assume no* responsibility for errors or omissions. No liability is assumed for* incidental or consequential damages in connection with or arising out of the* use of the information or programs contained herein."* * Permission is granted for this code to be used for educational purposes in* association with the book, given proper citation if and when posted or* reproduced.Any commercial use of this code requires the explicit written* permission of the publisher, Addison-Wesley Professional, a division of* Pearson Education, Inc. Send your request for permission, stating clearly* what code you would like to use, and in what specific way, to the following* address: * *  Pearson Education, Inc.*    Rights and Permissions Department*  One Lake Street*    Upper Saddle River, NJ  07458*  Fax: (201) 236-3290
*/ #include <cstring>
using std::strlen;#include <algorithm>
using std::copy; #include <cstddef>
using std::size_t; #include <iostream>
using std::ostream; #include <utility>
using std::swap;#include <initializer_list>
using std::initializer_list;#include <memory>
using std::uninitialized_copy;#include "string.h"// define the static allocator member
std::allocator<char> String::a;// copy-assignment operator
String & String::operator=(const String &rhs)
{// copying the right-hand operand before deleting the left handles self-assignmentauto newp = a.allocate(rhs.sz); // copy the underlying string from rhsuninitialized_copy(rhs.p, rhs.p + rhs.sz, newp);if (p)a.deallocate(p, sz); // free the memory used by the left-hand operandp = newp;    // p now points to the newly allocated stringsz = rhs.sz; // update the sizereturn *this;
}// move assignment operator
String & String::operator=(String &&rhs) noexcept
{// explicit check for self-assignmentif (this != &rhs) {if (p)a.deallocate(p, sz);  // do the work of the destructorp = rhs.p;    // take over the old memorysz = rhs.sz;rhs.p = 0;    // deleting rhs.p is saferhs.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<char> il)
{// no need to check for self-assignmentif (p)a.deallocate(p, sz);        // do the work of the destructorp = a.allocate(sz = il.size()); // do the work of the copy constructoruninitialized_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 Stringret.p = String::a.allocate(ret.sz); // allocate new spaceuninitialized_copy(lhs.begin(), lhs.end(), ret.p); // copy the operandsuninitialized_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;
elses = Sales_data(); //input failure,object is set to the default statereturn 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;
elses = Sales_data(); //input failure,object is set to the default statereturn 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 <string>
#include <iostream>
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 <string>
#include <iostream>
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.16

strvec类:

bool operator==(const StrVec &lhs,const StrVec &rhs){
if(lhs.size() == rhs.size()){return false;}for(auto iter1 = lhs.begin(),iter2 = rhs.begin(); iter1 != lhs.end() && iter2 != rhs.end();++ iter1,++iter2){if(*iter1 != *iter2){return false;}return true;
}
}

strblob类:

friend bool operator==(const StrBlob &,const StrBlob &);
friend bool operator!=(const StrBlob &,const StrBlob &);bool operator==(const StrBlob & lhs,const StrBlob &rhs)
{return lhs.data == rhs.data;
}
bool operator!=(const StrBlob &lhs,const StrBlob &rhs)
{return !(lhs == rhs);
}

strblobptr类:

bool operator==(const StrBlobPtr &lhs,const StrBlobPtr &rhs)
{
auto l = lhs.wptr.lock(),r = rhs.wptr.lock();
if(l == r){return  (!r || lhs.curr == rhs.curr); }else return false;
}

如果一个条件为真时不求另一个表达式的值,一个条件为假时才去求另一个表达式的值,那么用||符号。

习题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不同的对象经比较是不想等的,但是其中的任何一个都不小于另一个。然而实际情况是,如果我们有两个对象并且一个都不比另一个小,则从道理上来说这两个对象应该是相等的。

String比较两个字符串的字典顺序的先后。

这个链接有关于String类的实现的一个版本,个人觉得很好

C++ Primer 第三章补充String类的实现

bool operator<(const String &lhs,const String &rhs)
{return strcmp(s1.str,s2.str) < 0;
}
bool operator<=(const String &s1,const String &s2)
{return strcmp(s1,s2) <= 0;
}
bool operator>(const String &s1,const String &s2)
{return strcmp(s1,s2)>0;
}bool operator>=(const String &s1,const String &s2)
{return strcmp(s1,s2)>=0;
}

比较两个strblob的大小:

bool operator<(const StrBlob &s1,const StrBlob &s2)
{return *s1.data < *s2.data;
}
bool operator<=(const StrBlob &s1,const StrBlob &s2)
{return *s1.data <= *s2.data;
}
bool operator>(const StrBlob &s1,const StrBlob &s2)
{return *s1.data > *s2.data;
}
bool operator>=(const StrBlob &s1,const StrBlob &s2)
{return *s1.data >= *s2.data;
}

比较两个strblobptr:

bool operator<(const StrBlobPtr &s1,const StrBlobPtr &s2)
{
auto l = s1.wptr.lock(), r  = s2.wptr.lock();
if(l == r){if(!l)return false;  //两个指针都为空,认为是相等的return s1.curr < s2.curr;}
elsereturn false;
}
bool operator<=(const StrBlobPtr &s1,const StrBlobPtr &s2)
{
auto l = s1.wptr.lock(), r = s2.wptr.lock();
if(l == r){if(!l)    return true;return s1.curr <= s2.curr;}
else return false;
}
bool operator>(const StrBlobPtr &s1,const StrBlobPtr &s2)
{
auto l = s1.wptr.lock(), r = s2.wptr.lock();
if(l == r){//两个指针都为空,认为是相等的。if(!r)return false;return (s1.curr > s2.curr);}
elsereturn    false;
}
bool operator>=(const StrBlobPtr &s1,const StrBlobPtr &s2)
{
auto l = s1.wptr.lock(),r = s2.wptr.lock();
if(l == r){return (!r || s1.curr >= s2.curr); //当!r为假时才求解后面的表达式的值}
else return false;}

要比较两个strvec,需要手工编写代码,逐个比较string。

        别忘记了,有长短之分。

14.19

在7.40中,我们编写了类Date,对于日期,可以比较其大小,因此需要为此重载关系运算符。
class Date
{
...
};
bool operator<(const Date & d1,const Date &d2)
{
return (d1.year < d2.yera) ||(d1.year == d2.year && d1.month < d2.month) || (d1.year == d2.year && d1.month == d2.month && d1.day < d2.day);
}
bool operator<=(const Date &d1,const Date &d2)
{return  d1 == d2 || d1 < d2;
}
bool operator>(const Date &d1,const Date &d2)
{return !(d1 <= d2);
}
bool operator>=(const Date &d1,const Date &d2)
{return !(d1 < d2);
}

14.4节练习

14.20

+运算符不是成员函数,+=是成员函数:

Sales_data operator+(const Sales_data &rhs,const Sales_data &rhs)
{Sales_data sum = lhs;sum += rhs;return sum;
}
Sales_data & Sales_data::operator+=(const Sales_data &s)
{units_sold += s.units_sold;revenue += s.revenue;return *this;
}

14.21

Sales_data  opeartor+(const Sales_data s1,const Sales_data &s2)
{
Sales_data sum = s1;
sum.units_sold += s2.units_sold;
sum.revenue += s2.revenue;return sum;
}
Sales_data & operator+=(const Sales_data s)
{
*this = *this + s;
return *this;
}

14.22

Sales_data & operator=(const string &s)
{bookNo = s;return *this;
}

14.23

StrVec & operator=(initializer_list<string> li)
{
auto data = alloc_n_copy(li.begin(),li.end());
free();
elements = data.first;
cap = first_free = data.second;return *this;
}

14.24

例子7.40中曾经选择并且编写了一个类,它有3个int类型的数据成员,利用浅拷贝就可以,不需要另外定义拷贝赋值和移动赋值运算符。

14.25

这完全取决于实际的需求,例如,如果你希望能用string形式的日期来初始化Date,就需要定义一个接受string的赋值运算符。

class Date{
public:Date & operate=(const string &date);
};
Date & Sales_data::operator=(const string &date){//接受 1971-12-5 类型的日期istringstream in(date);char ch1,ch2;in >> year >> ch1 >> month >> ch2 >> day;if(!in || ch1 != '-' || ch2 != '-')throw invalid_argument("Bad date");if(month < 1  || month > 12 || day < 1 || day > 31)throw invalid_argument(“Bad date”);return *this;}

显然,日期的合法性并不完美,读者可以自己尝试改进。

练习14.26:为你的StrBlob类、StrBlobPtr类、StrVec类和String类定义下标运算符。

解:

class StrBlob{public:string &operator[](size_t n){return data[n];}const string & operator[](size_t n)const{return (*data)[n];}
};
class StrBlobPtr{
public:string &operator[](size_t n){return (*wptr.lock())[n];}const string &operator[](size_t n)const{return (*wptr.lock()[n]]);}private://..其他成员};
class StrVec{string& operator[](size_t n){return elements[n];}}

以下这个题目自己很不理解。

class String {public:char& operator[](size_t n){return (char)str[n];}private:char *str;}

St练习14.27.为了你的StrBlobPtr类添加递增和递减运算符。

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(curr,"increment past end of StrBlobPtr");retutn *this;        }
StrBlobPtr operator++(int)
//后置运算符有个参数int,这是区别后置和前置的一个伪参数,不会用
{StrBlobPtr ret = *this;++ *this;return ret;
}
//后置的--运算符也有一个int类型运算符
StrBlobPtr operator--(int)
{StrBlobPtr ret = *this;-- *this;              //这个是调用前置类型的--运算符,检查也叫给了那个函数。return ret;
}

这个问题要注意,当++时候,先执行check(),因为一个StrBlobPtr对象的curr变量的取值范围是[0,data.size()],如果先执行++,那么就会出现越界的可能性。所以此时必须要检查curr是否大于等于data.size(),如果大于等于data.size(),那么应该立刻抛出异常。

但是--时候情况不一样,执行--运算前,curr可能的值范围是[0,data.size()],做--运算是安全的(除了curr是0的情况)。所以先执行--运算,但是如果curr原来是0,那么执行后curr会是一个超级大的数(curr是size_t也就是unsigned long类型,所以如果原来是0,那么执行减法后会得到超级大的数,肯定会大于data.size(),肯定会check中抛出异常),所以执行++运算时,先check,后执行++,执行--运算时,先执行--运算,然后再check(),这样是绝对安全的。

14.28  为你的StrBlobPtr类添加加法和减法运算符,使其可以实现指针的算数运算。

class  StrBlobPtr
{StrBlobPtr operator+(int n);StrBlobPtr operator-(int n);//其它成员
}StrBlobPtr StrBlobPtr::operator+(int n){auto ret = *this;ret.curr += n;return ret;}StrBlobPtr StrBlobPtr::operator-(int n){auto ret = *this;ret.curr += n;return ret;}

有了上述的两个函数,就可以执行指针的算数运算了。

练习14.29

对于++和--运算符,无论是 hi前缀版本还是后缀版本,都会改变对象本身的值,因此不能定义为const的。

练习14.30

class StrBlobPtr
{
public:string & operator*(){auto p = check(curr,"deferenced past the end.");return (*p)[curr];}string & operator->()const{return &(this->operator*());  //成员运算符返回解引用运算符的地址。}
};
class ConstStrBlobPtr
{
public:const string & operator*()const{auto p = check(curr,"deference past the end.");return (*p)[curr];}const string*operator->()const{return &(this->operator*());}
};

到底什么能够决定返回的引用是加const还是不用加const?

根据这个例子,如果是一个容器类型,那么返回元素的引用到底是否加const,那么取决于这个容器的类型是不是const的。

14.31

对于StrBlobPtr类型,那么它的数据成员有2个:分别是weak_ptr<vector<string>>和size_t类型的,前者定义了自己的拷贝构造函数、赋值运算符和析构函数,后者是内置类型的,因此默认的拷贝语义即可,无须为了StrBlobPtr定义拷贝构造函数、赋值运算符和析构函数。

14.32

定义一个类令其含有指向StrBlobPtr对象的指针,为这个类定义重载的箭头运算符。

class MyClass
{public:string* operator*()const{return ptr->operaotr->();}private:StrBlobPtr *ptr;
};

14.8节基础知识

#include <string>
#include <iostream>
#include <vector>
using namespace std;
class PrintString {public:PrintString(ostream &o = cout,char c = ' '):os(o),sep(c){}void operator()(const string &s){os << s << sep;        }private:ostream &os;char sep;};
int main()
{
string s = "this is string s...";
PrintString p;
p("this is string s...");
PrintString errors(cerr,'\n');
errors(s);return 0;
}

运行结果:

this is string s... this is string s...

14.32

class MyClass
{public:string * operator->()const{return ptr->operator->();}private:StrBlobPtr *ptr;
}:

14.33

0个或者多个。

这里for_each里面放着一个临时构造的类对象(重载了调用运算符的类又叫函数对象),这个函数对象可以被调用。可以直接放在for_each算法的最后面。

14.33

答:0个或者多个。

14.34

答:

#include <string>
#include <iostream>
#include <memory>
using namespace std;
struct Select{public:Select() = default;int operator()(int a,int b,int c){return a ? b : c;        }private:};int main()
{
Select s1;
cout << s1(1,2,3)  << endl; return 0;
}

14.35

#include <string>
#include <iostream>
#include <memory>
using namespace std;struct Read{
Read() = default;
string  operator()(istream &is,string &s){getline(is,s);if(!is)return "";return s;
}
};
int main()
{
Read r;
string s;
string str = r(cin,s);
cout << str << endl;return 0;
}

或者可以这样写:

#include <string>
#include <iostream>
#include <memory>
using namespace std;struct Read{Read(istream &in = cin):is(in){}string  operator()(){string s;getline(is,s);if(!is)return "";return s;}private:istream &is;
};
int main()
{
Read r;
string s = r();
cout << s << endl;return 0;
}

总之,类的重载调用运算符函数可以自己带参数进行相关操作,也可以依靠其它数据成员来进行相关的操作。函数对象类的其它数据成员就是辅助进行相关的调用运算符的操作的。

练习14.36

#include <string>
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
struct Select{
Select(istream &in = cin):is(in){}
vector<string> operator()(){string str;vector<string> v;while(getline(is,str)){v.push_back(str);            }return v;  }
private:istream &is;
};int main()
{Select s;auto p =s();for(auto &i : p)cout << i << endl;
}

14.37

#include <string>
#include <iostream>
#include <memory>
#include <algorithm>
#include <vector>
using namespace std;struct IsEqual{IsEqual() = default;IsEqual(int a):val(a){}bool operator()(int a){return a == val;        }int val = 1;
};int main()
{
vector<int> v{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};replace_if(v.begin(),v.end(),IsEqual(),100);
for(auto i : v)cout << i << endl;return 0;
}

调用运算符的例子:

#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace  std;
class IntComp{public:IntComp(int v):val(v){}bool operator()(int n){return val == n;}private:int val;
};int main()
{const int old_value  = 2;
const int new_value  = 200;
IntComp icmp(old_value);
vector<int> v{1,2,3,4,5,67,7,8,9,19};
replace_if(v.begin(),v.end(),IntComp(3),new_value);
for(auto i : v)cout << i << endl;return 0;
}

14.8.1节练习

14.38

自己编写答案:

#include <string>
#include <iostream>
#include <algorithm>
#include <vector>
#include <fstream>
#include <sstream>
using namespace std;
class Stati_word_len{public:Stati_word_len(const string & s):val(s){}//判断这个对象的长度是否和n相等bool operator()(int n){return n == val.size();}private:string val;
};//对于文件流f,统计输入的单词中有多少个长度是1,2,3,4,5...10
void  stati_words(fstream &f)
{
string line;
size_t lengthes[10] = {0};
int len = 0;
while(getline(f,line)){string word;stringstream in(line);while(in >> word){Stati_word_len w(word);for(int i = 1; i != 11; ++i){if(w(i))lengthes[i-1] ++;}       }   }
f.close();
for(auto i = 0; i != sizeof(lengthes) / sizeof(lengthes[0]); ++ i){cout << lengthes[i] << endl; }
}
int main()
{fstream in("data.txt");
stati_words(in);return 0;
}

参考书的答案:

#include <string>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
/*比较单词的长度是否与一个阔值相等*/
class StrLenIs{public:StrLenIs(int len):len(len){}bool operator()(string word){return word.size() == len;}private:int len;
};void readStr(istream &is,vector<string> &vs)
{
string word;
while(cin >> word){vs.push_back(word);}
}int main()
{
vector<string> v1;
readStr(cin,v1);
for(auto i = 1;i != 11; ++ i){cout<< "len "<<i<<":" <<count_if(v1.begin(),v1.end(),StrLenIs(i)) << endl;}return 0;
}

14.39

#include <string>
#include <iostream>
#include <memory>
#include <fstream>
#include <sstream>
using namespace std;
class StatiWord{public:StatiWord(const string &w):word(w){}size_t operator()(){return word.size();}private:string word;
};int main()
{
fstream in("data.txt");
string line;
size_t lengths[10] = {0};
while(getline(in,line)){stringstream s(line);string word;while(s >> word){StatiWord st_1(word);size_t len = st_1();if(len >=1 && len <= 9)lengths[len - 1]++;else lengths[9]++;}}
for(auto i : lengths){cout << i <<endl;}return 0;
}

14.39参考书答案

#include <string>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
class StrLenBetween{public:StrLenBetween(int min,int max):minLen(min),maxLen(max){}bool operator()(const string &w){return w.size() >= minLen && w.size() <= maxLen;    }private:size_t minLen;size_t maxLen;
};
class StrNotShorterThan{public:StrNotShorterThan(int s):len(s){}bool operator()(const string &s){return s.size() >= len;}private:size_t len;};int main()
{
vector<string> v{"str1","str2","str3","str4","str5","str6","str7","str8","12"};
StrLenBetween b1(1,9);
StrNotShorterThan b2(10);
cout << "length between 1 and 9:" << count_if(v.begin(),v.end(),b1) << endl;
cout << "length notshorter than:" << count_if(v.begin(),v.end(),b2) << endl;return 0;
}
length between 1 and 9:9
length notshorter than:0

14.40

#include <string>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;string make_plural(size_t n,const string &s,const string & s1 = "s");class compLen{public:bool operator()(const string &s1,const string &s2){//这里必须<,不能<=,否则stable_sort不起作用//还可能给按照降序排列。return s1.size() < s1.size();           }
};class IsLonggerThan{public:IsLonggerThan(int l):sz(l){}bool operator()(const string &s){  return s.size() >= sz;}private:int sz;
};class PrintString{public:void operator()(const string &s){cout << s << " ";}};void elimDups(vector<string> &words)
{
//按字典顺序排列words,以便查找重复单词
sort(words.begin(),words.end());
//unique()重排输入范围,是的每个单词都只出现一次
auto end_unique = unique(words.begin(),words.end());
//使用向量操作erase删除重复单词
words.erase(end_unique,words.end());
}void biggies(vector<string> &words,vector<string>::size_type sz)
{
elimDups(words);//no problems
#ifdef DEBUG
for(auto i : words)cout << i << endl;
#endif//按照长度排序,长度相同的单词维持字典顺序
stable_sort(words.begin(),words.end(),compLen());#ifdef DEBUG2
for(auto i = 0; i != words.size() ; ++i)cout << words[i] << endl;
#endif//获取一个迭代器,指向第一个满足 size() >=sz的元素
auto wc = find_if(words.begin(),words.end(),IsLonggerThan(sz));
//计算满足size() >= sz的元素的数目
auto count = words.end() - wc;
cout << count << " " << make_plural(count,"word","s") <<" of length " << sz<< " or  longer" << endl;
//打印长度大于或等于给定值的单词,每个单词后面接一个空格
for_each(wc,words.end(),PrintString());
cout << endl;}
string make_plural(size_t n,const string &s,const string & s1)
{return (n > 1) ? (s + s1) : (s);
}int main()
{
vector<string> v = {"asdafasdf","gr","lmjhjk","hjoi","adff","bnmijdf","bnmtsdf","kklsd","xaf","ywlaijsdf","xafd"};
biggies(v,5);return 0;
j}

14.41

在c++11中,lambda是通过匿名的函数对象来实现的,因此我们可以把lambda看作是对函数对象在使用方式上的简化。

当代码需要一个简单的函数,并;且这个函数并不会在其它地方使用的时候,我们就可以使用lambda来实现,此时它所起的作用类似匿名函数。

但如果这个函数需要多次使用,并且他需要存放某些状态的话,使用函数对象则更合适一些。

14.8.2节基础知识

14.42(a)

#include <string>
#include <iostream>
#include <algorithm>
#include <functional>
#include <vector>
using namespace std;
using namespace std::placeholders;
int main()
{
vector<int> v{12,3,12345,12,15433,123,123111};
auto sum = count_if(v.begin(),v.end(),bind(greater<int>(),_1,1024));
cout << sum << endl;return 0;
}

提示:bind定义在头文件functional中

占位符,_1定义在头文件functional中,定义在命名空间std::spaceholders中,spaceholders中。

因此要使用bind和_1,必须程序中加:

#include <functional>
using namespace std::spaceholders;

14.42(b)

#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
using namespace std::placeholders;
int main()
{
vector<string> v{"pooth","pooth","abd","abdf","abg","abasdfasdfk","asdfjkasd"};
auto ojb_find = bind(not_equal_to<string>(),_1,"pooth");
auto pos = find_if(v.begin(),v.end(),ojb_find);
cout << *pos << endl;return 0;
}

14.42(c)

#include <string>
#include <functional>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
using namespace std::placeholders;int main()
{
vector<int> v{1,2,3,4,5,6,7,8,9,10};
transform(v.begin(),v.end(),v.begin(),bind(multiplies<int>(),_1,2));
for(auto i : v)cout << i << endl;return 0;
}

能不能用for_each?for_each能否改变原来的元素?

14.8.3

#include <string>
#include <iostream>
#include <memory>
#include <functional>
#include <map>
using namespace std;
using namespace std::placeholders;
int add(int i,int j){ return i + j;}
struct Mod{
int operator()(int i,int j){return i % j;}
};struct divide{
double operator()(int i,int j){return (double)i / j;}
};int main()
{
function<int(int,int)> f = add;
function<int(int,int)> f1 = Mod();
function<int(int,int)> f2 = [](int i,int j){ return i % j;};
cout << f(4,2) << endl;
cout << f1(4,2) << endl;
cout << f2(4,2) << endl;map<string,function<int(int,int)>> binops
{
{"+",add},
{"-",std::minus<int>()},
{"*",[](int i ,int j){return i * j;}},
{"/",divide()},
{"%",Mod()}
};binops["+"](1,2); //call add(1,2)
binops["-"](2,1); //call minus(2,1)
binops["*"](2,1); //call multiplies(2,1)
binops["/"](2,1); //call divide(2,1)
binops["%"](2,1); //call Mod(2,1)return 0;
}

练习14.44

#include <string>
#include <iostream>
#include <algorithm>
#include <functional>
#include <map>
using namespace std;
using namespace std::placeholders;int main()
{
map<string,function<int(int,int)>> binOps =
{{"+",std::plus<int>()},{"-",std::minus<int>()},{"*",std::multiplies<int>()},{"/",std::divides<int>()},{"%",std::modulus<int>()}
};int i,j;
string ops;
cin >> i >> ops >> j;
cout << binOps[ops](i,j) <<endl;return 0;
}

14.45

转换成string返回值应该是bookNO;

如果要转换成double,返回值是revenue;

14.46

答:
不该;因为Sales_data有三个数据成员,分别是bookNo,revenue和units_sold。三个运算符在一起才有意义。

但是如果确实需要定义这两个运算符的话,那么应该把他们申明称为explicit的,这样可以防止Sales_data在某些情况下被默认转换成string或者double类型的,这样可能导致我们意料之外的运算结果。

14.47

答:

第一个转换成const int类型,第二个转换成int类型。

14.48

答:

我们应该为Date类型定义一个类型转换运算符,用来检查3个数据成员是否都是有效值(比如month是否是1~12之间,day是否超出了当月的天数)。

bool类型转换运算符应该声明为explicit的,因为哦我们有意要在条件表达式中使用他们。

14.48

答:

class Date{
//其它成员函数...
explicit operator bool(){return month >= 1 && month <=12 && }};

14.49

class Date{
public:
explicit operator bool(){
vector<vector<int>> days_per_month = {{31,28,31,30,31,30,31,31,30,31,30,31},{31,29,31,30,31,30,31,31,30,31,30,31}};
}
operator bool(){
return (month >=1 && month <= 12 && (
day >= 1 && day <= days_per_month[isLeapYear() ? 1:0][month-1]
))
}bool isLeapYear()
{return (year % 4 == 0 && year % 400 !=0 || year % 400 ==0)
}
};

14.9.2

c++ primer 第14章 习题解答相关推荐

  1. c语言程序设计教程第三版答案9.5,C语言程序设计-第5-9章习题解答.ppt

    C语言程序设计-第5-9章习题解答 华中科技大学计算机学院 C语言程序设计 第5-9章 部分习题解答 作业中问题较多的习题 第5章5.4(2) 第6章6.1,6.3,6.5(6),6.8 第7章无 第 ...

  2. c语言与单片机技术试卷与答案,哈尔滨工业大学《单片机原理及应用》课件、各章习题解答、试题及答案...

    哈尔滨工业大学<单片机原理及应用>课件.各章习题解答.试题及答案等 本光盘依托张毅刚等编著,高等教育出版社出版的高等学校教材<单片机原理及应用>制作的.主要包括电子教案及各种电 ...

  3. c语言判断正整数位数 请用strengh,C语言程序设计-4、12章习题解答.doc

    C语言程序设计-4.12章习题解答 C语言程序设计概述 一个C程序的执行是从 A . A.从main()函数开始,直到main()函数结束B.第一个函数开始,直到最后一个函数结束C.第一个语句开始,直 ...

  4. python语言程序设计梁勇-Python语言程序设计(美-梁勇)第1章习题解答

    Python语言程序设计(美-梁勇)第1章计算机.程序和Python概述习题解答 第一章 计算机.程序和python概述 1.1什么是硬件?什么是软件? 答:硬件包括计算机上能看到的物理元素,而软件提 ...

  5. 【虎书】Fundamentals of Computer Graphics(Fourth Edition)第二章习题解答

    [虎书]Fundamentals of Computer Graphics(Fourth Edition)第二章章末习题解答 Exercises Exercise 1: Cardinality of ...

  6. 第15章习题解答(一)——《x86汇编语言:从实模式到保护模式》读书笔记40

    1. 第15章代码修改 先不说习题,说一说我对源码的修改.从运行结果来看,主要是增加了颜色支持.不过把我的代码与配书代码相比较的话,还是有很多不同的.这些修改是怎么来的,可以参考我之前的博文. 运行效 ...

  7. DIP第一章习题解答

    第1章 习题(数字图像处理) 问:(1)什么是数字图像处理,数字图像处理的发展历程.主要应用以及未来发展方向? 答:1.数字图像处理就是一种利用数字计算机去处理所获取信息的技术.(即图像的获取.传输. ...

  8. 《神经网络与深度学习》第8-9章习题解答

    最近忙毕设论文,之前写的第8-9章个人解答也就从自己的私人博客进行转载到CSDN上进行分享,答案的正确性不能完全保证. 第八章 8-1 LSTM输入层的特征长度为 n n n,输出层的长度为 m m ...

  9. C++ primer 14章习题答案

    14.1节 练习14.1 相同点:对于优先级和结合型以及操作数的数目都不变. 不同点:重载操作符必须具有至少一个class或枚举类型的操作数. 14.2 下面所有的代码行可能把好几个文件放在一起了. ...

最新文章

  1. Spring Boot 整合Pagehelper(为什么PageHelper分页不生效)
  2. NR 5G RRC连接重建
  3. phy芯片测试寄存器_PCIe 5.0首秀!7nm IP方案已成熟!PCIe 5.0的芯片设计有多难?...
  4. c++一个问题:while(!cin) 的解释
  5. mysql 视图 数据相加_MySQL
  6. linux 串口命令
  7. 阿里云数据库使用初体验
  8. 机械加工行业QC数据采集与CPK分析案例分享
  9. ITIL4中的三个基本概念
  10. 爱搞事情:关于黑苹果Intel网卡驱动这件事
  11. golang学习之远程木马demo编写
  12. vue生命周期的快速记忆方法
  13. VMware Workstation 14中文破解版下载(附密钥)(笔记)
  14. android强行打开软键盘,隐藏Android软键盘(如果已打开)
  15. Flex布局和gird布局
  16. 头条号自媒体运营:如何在今日头条涨500+粉丝?
  17. 设计师常用的7款界面设计工具!
  18. 服务器推送小程序,小程序之主动推送消息(订阅消息)
  19. 关于mysql数据库回表的粗浅理解
  20. HTML5+CSS3开发-胡杨柳依-专题视频课程

热门文章

  1. nodejs 框架 中文express 4.xxx中文API手册
  2. 【和我一起学习Unity3D】Unity3D的坐标控制
  3. Java 并发专题 :FutureTask 实现预加载数据 在线看电子书、浏览器浏览网页等
  4. 进程、线程与任务程序之间的关系
  5. data too long for column的解决方法
  6. 【数据结构与算法】之深入解析“最长连续序列”的求解思路与算法示例
  7. 大数据WEB阶段(九)Myeclipse中配置Tomcat并发布项目
  8. 【机器视觉】Qt联合Halcon编程之显示多图片
  9. 【机器视觉】 assign_at算子
  10. 【Protocol Buffer】Protocol Buffer入门教程(五):repeated限定修饰符