本文答案,部分参考于C++ Primer 习题集
前面章节的习题答案
配套的学习资料
https://www.jianguoyun.com/p/DTK5uJgQldv8CBjKv80D
14.1
重载运算符必须具有至少一个class或枚举类型的操作数
重载操作符不保证操作数的求值顺序.例如&&和||的重载版本不再具有“短路求值"的特性.两个操作数都要进行求值.而且不规定操作数的求值顺序.
相同点:对于优先级和结合性及操作数的数目都不变.
14.2
friend std::istream& operator>>(std::istream&, Sales_data&); //>>的声明
friend std::ostream& operator<<(std::ostream&, Sales_data&); //<<的声明
Sales_data operator+=(const Sales_data&);
Sales_data operator+(const Sales_data&, const Sales_data&);
14.3
(a)使用的是C++语言内置版本的==,比较两个指针
(b) 引用了string版本的重载
© 引用了vector版本的重载
(d) 应用了string 版本的重载== ,字符串字面常量被转换为string
吐槽(d) 书上的打印错误了
应该是
svec1[0]=“stone”
14.4
(a) %通常定义为非成员
(b) %=通常定义为非类成员.因为它非改变对象的状态.
© ++通常定义为类成员.因为它会改变对象的状态.
(d) ->必须定义为类成员.否则编译会报错.
(e) <<通常定义为非成员
(f)&&通常定义为非成员
(g)==通常定义为非成员
(h) ()必须定义为类成员.否则编译会报错.
14.5
#include<iostream>
using namespace std;
class Data {
public:
Data() {}
Data(int y, int m, int d) { year = y; month = m; day = d; }
friend ostream& operator<<(ostream& os, const Data& dt);
private:
int year, month, day;
};
ostream& operator<<(ostream& os, const Data& d) {
const char sep = '\t';
os << "year:" << d.year << sep << "month:" << d.month << sep << "day:" << d.day << endl;
return os;
}
int main(void) {
Data T(2020, 10,9);
cout << T;
return 0;
}
输入输出
year:2020 month:10 day:9
14.6
std::ostream& operator<<(std::ostream& os, const Sales_data& item) {
char sep = ' ';
os << item.isbn() << sep << item.units_sold << sep << item.revenue ;
return os;
}
14.7
ostream* operator<<(ostream& os, const String& str) {
cout << str;
return os;
}
14.8
同14.5
14.9
std::istream& operator>>(std::istream& is, Sales_data& item) {
double price;
is >> item.bookNo >> item.units_sold >> price;
if (is) item.revenue = item.units_sold * price;
else item = Sales_data();
return is;
}
14.10
(a)是合法的.输入一个书的编码.两个价格.赋值运算符正常工作
(b)是非法的.上面的程序会走别的路.值成为默认的初始化.
14.11
没有判断输入的正确性.万一输入的是14.10的(b)的情况呢.
14.12
感觉这种检查都是一样的模板的.
主要就是判断一下is的类型对不对.
代码如下:
#include<iostream>
using namespace std;
class Data {
public:
Data() {}
Data(int y, int m, int d) { year = y; month = m; day = d; }
friend ostream& operator<<(ostream& os, const Data& dt);
friend istream& operator>>(istream& is, Data& dt);
private:
int year, month, day;
};
ostream& operator<<(ostream& os, const Data& d) {
const char sep = '\t';
os << "year:" << d.year << sep << "month:" << d.month << sep << "day:" << d.day << endl;
return os;
}
istream& operator>>(istream& is, Data& d) {
is >> d.year >> d.month >> d.day;
if (!is) d = Data(0, 0, 0);
return is;
}
int main(void) {
Data T(2020, 10, 9);
Data Test;
cin >> Test;
cout << Test;
return 0;
}
输出结果如下:
1998 10 9
year:1998 month:10 day:9
14.13
可以写一个减法的运算符
friend Sales_data operator-(const Sales_data& lhs, const Sales_data& rhs);
Sales_data operator-=(const Sales_data& rhs);
Sales_data operator-(const Sales_data& lhs, const Sales_data& rhs) {
Sales_data sub = lhs;
sub -= rhs;
return sub;
}
Sales_data& Sales_data::operator-=(const Sales_data& rhs) {
units_sold -= rhs.units_sold;
revenue -= rhs.revenue;
return *this;
}
14.14
显然,从头实现operator+的方式与借助operator+=实现的方式相比.在性能上没有优势.而可读性上后者显然更好.因此.在此例中代码复用是最好的方式.
14.15
写的类是Data.算术运算对Data.日期来讲,没有太大意义.
14.16
这里简单的写一个StrBlob的.
friend bool operator==(const String&, const String&);
friend bool operator!=(const String&, const String&);
bool operator==(const String& lhs, const String& rhs) {
return strcmp(lhs.str, rhs.str);
}
bool operator!=(const String& lhs, const String& rhs) {
return !(lhs == rhs);
}
14.17
bool operator==(const Data& lhs, const Data& rhs) {
return lhs.year == rhs.year && lhs.month == rhs.month && lhs.day == rhs.day;
}
bool operator!=(const Data& lhs, const Data& rhs) {
return !(lhs == rhs);
}
14.18
这里还是以String为例
friend bool operator<(const String& s1, const String& s2);
friend bool operator<=(const String& s1, const String& s2);
friend bool operator>(const String& s1, const String& s2);
friend bool operator>=(const String& s1, const String& s2);
bool operator<(const String& s1, const String& s2) {
return strcmp(s1.str, s2.str) < 0;
}
bool operator<=(const String& s1, const String& s2) {
return strcmp(s1.str, s2.str) <= 0;
}
bool operator>(const String& s1, const String& s2) {
return strcmp(s1.str, s2.str) > 0;
}
bool operator>=(const String& s1, const String& s2) {
return strcmp(s1.str, s2.str) >= 0;
}
14.19
bool operator>(const Data& d1, const Data& d2) {
if (d1.year < d2.year) return true;
else if (d1.year > d2.year) return false;
else {
if (d1.month < d2.month) return true;
else if (d1.month > d2.month) return false;
else {
if (d1.day < d2.day) return true;
else return false;
}
}
}
bool operator<(const Data& d1, const Data& d2) {
if (d1 == d2) return false;
return !(d1 > d2);
}
14.20
friend Sales_data operator+(const Sales_data& lhs, const Sales_data& rhs);
Sales_data& operator +=(const Sales_data& rhs);
Sales_data operator+(const Sales_data& lhs, const Sales_data& rhs) {
Sales_data sum = lhs;
sum += rhs;
return sum;
}
Sales_data& Sales_data::operator+=(const Sales_data& rhs) {
units_sold += rhs.units_sold;
revenue += rhs.revenue;
return *this;
}
14.21
没有必要,性能一般,可读性一般
Sales_data& Sales_data::operator+=(const Sales_data& rhs) {
*this = (*this) + rhs;
}
14.22
Sales_data& operator=(const string& isbn);
Sales_data& Sales_data::operator=(const string& isbn) {
bookNo = isbn;
return *this;
}
14.23
StrVec& operator=(std::initializer list<std::string> il);
StrVec&StrVec::operator=(std::initializer_list<std::string> il){
auto data=alloc_n_copy(il.begin(),il.end());
free();
elements=data.first;
first_free=cap=data.second;
return *this;
}
14.24
因为,Data只有三个数据,浅拷贝就够了.不需要深拷贝.
14.25
有许多,比如用一行String来初始化啊.等等。
14.26
public:
char& operator[] (std::size_t n) { return (char)str[n]; }
const char& operator[] (std::size_t n) const { return (char)str[n]; }
private:
char* str;
14.27
StrBlobPtr& StrBlobPtr::operator++() {
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
StrBlobPtr& StrBlobPtr::operator--() {
check(curr, "decrement pas begin of StrBlobPtr");
--curr;
return *this;
}
StrBlobPtr& StrBlobPtr::operator++(int) {
StrBlobPtr ret = *this;
++(*this);
return ret;
}
StrBlobPtr& StrBlobPtr::operator--(int) {
StrBlobPtr ret = *this;
--(*this);
return ret;
}
//前缀
StrBlobPtr& operator++();
StrBlobPtr& operator--();
//后缀
StrBlobPtr& operator++(int);
StrBlobPtr& operator--(int);
14.28
StrBlobPtr operator+(int n);
StrBlobPtr operator-(int n);
StrBlobPtr StrBlobPtr::operator+(int n) {
auto ret = *this;
ret.curr += n;
return *this;
}
StrBlobPtr StrBlobPtr::operator-(int n) {
auto ret = *this;
ret.curr -= n;
return *this;
}
14.29
脑子呢.
const就是常量.
const常量怎么定义成++ 或–呢.
14.30
string& operator*()const {
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
string* operator->() const {
return &(this->operator*());
}
14.31
对于StrBlobPtr类,它的数据成员有两个.分别是weak_ptr<vector> 和size_t类型的,前者定义了自己的拷贝构造函数,后者是内置类型.因此默认的拷贝语义即可.无须为StrBlobPtr定义拷贝构造函数,赋值运算符和析构函数.
14.32
class MyClass {
public:
string* operator->()const {
return ptr->operator->();
}
private:
StrBlobPtr* ptr;
};
14.33
0个或多个
14.34
class IfElseThen {
public:
IfElseThen() {}
IfElseThen(int i1, int i2, int i3) :iVal1(i1), iVal2(i2), iVal3(i3) {}
int operator()(int i1, int i2, int i3) { return i1 ? i2 : i3; }
private:
int iVal1, iVal2, iVal3;
private:
int iVal1, iVal2, iVal3;
};
14.35
class ReadString {
public:
ReadString(istream& is = cin) :is(is) {}
string operator()() {
string line;
if (!getline(is, line)) { line = " "; }
return line;
}
private:
istream& is;
};
14.36
void testReadString() {
ReadString rs;
vector<string> vec;
while (true) {
string line = rs();
if (!line.empty()) {
vec.push_back(line);
}
else { break; }
}
}
14.37
#include<iostream>
#include<string>
#include<vector>
#include <algorithm>
using namespace std;
class IntCompare {
public:
IntCompare(int v) :val(v) {}
bool operator()(int v) { return val == v; }
private:
int val;
};
int main(void) {
vector<int> vec = { 1,2,3,2,1 };
const int oldValue = 2;
const int newValue = 200;
IntCompare icmp(oldValue);
replace_if(vec.begin(), vec.end(), icmp, newValue);
return 0;
}
14.38
#include<iostream>
#include<string>
#include<vector>
#include <algorithm>
using namespace std;
class StrLenIs {
public:
StrLenIs(int len) :len(len) {}
bool operator()(const string& str) { return str.length() == len; }
private:
int len;
};
void readStr(istream& is, vector<string>& vec) {
string word;
while (is >> word) vec.push_back(word);
}
int main(void) {
vector<string> vec;
readStr(cin, vec);
const int minLen = 1;
const int maxLen = 10;
for (int i = minLen; i <= maxLen; ++i) {
StrLenIs slenIs(i);
cout << "len : " << i << ", cnt: " << count_if(vec.begin(), vec.end(), slenIs) << endl;
}
return 0;
}
14.39
#include<iostream>
#include<string>
#include<vector>
#include <algorithm>
using namespace std;
class StrLenIs {
public:
StrLenIs(int len) :len(len) {}
bool operator()(const string& str) { return str.length() == len; }
private:
int len;
};
void readStr(istream& is, vector<string>& vec) {
string word;
while (is >> word) vec.push_back(word);
}
class StrLenBetween {
public:
StrLenBetween(int minLen, int maxLen) :minLen(minLen), maxLen(maxLen) {}
bool operator()(const string& str) {
return str.length() >= minLen && str.length() <= maxLen;
}
private:
int minLen;
int maxLen;
};
class StrNotShorterThan {
public:
StrNotShorterThan(int len) :minLen(len) {}
bool operator()(const string& str) { return str.length() >= minLen; }
private:
int minLen;
};
int main(void) {
vector<string> vec;
readStr(cin, vec);
StrLenBetween slenBetween(1, 9);
StrNotShorterThan sNotShorterThan(10);
cout << "len 1~9:" << count_if(vec.begin(), vec.end(), slenBetween) << endl;
cout << "len>=10:" << count_if(vec.begin(), vec.end(), sNotShorterThan) << endl;
return 0;
}
输入输出如下:
Link Love Zelda
I am you father safdsfdsafsdafsa
^Z
len 1~9:7
len>=10:1
14.40
----------------占位符 ---------------
14.41
在C++11中,lamba是通过匿名的函数对象来实现的.因此我们可以吧lambda看作是对函数对象使用方式上的简化.
当代码需要一个简单的函数,并且这个函数并不会在其他地方被使用时,就可以使用lambda来实现.此时它所起的作用类似与匿名函数
但如果这个函数需要多次调用.并且它需要保存某些状态的话,使用函数对象则更合适一些.
14.42
count_if(vec.begin(), vec.end(), bind2nd(greater<int>(), 1024));
find_if(vec.begin(), vec.end(), bind2nd(not_equal_to<string>(), "pooh"));
transform(vec.begin(), vec.end(), vec.begin(), bind2nd(multiplies<int>(), 2));
14.43
bool dividedByALL(vector<int>& ivec, int dividend) {
return count_if(ivec.begin(), ivec.end(), bindlst(modulus<int>, dividend)) == 0;
}
14.44
#include<iostream>
#include<string>
#include<vector>
#include <algorithm>
#include <functional>
#include<map>
using namespace std;
map<string, function<int(int, int)>>binOps = {
{"+",plus<int>()},
{"-",minus<int>()},
{"*",multiplies<int>()},
{"/",divides<int>()},
{"%",modulus<int>()}
};
int main(void) {
int a, b;
string op;
cin >> a >> op >> b;
cout << binOps[op](a, b) << endl;
return 0;
}
输入输出如下:
1 * 2
2
14.45
① 转换成string .返回值是bookNo
② 转换成double.返回值是revenue
14.46
Sales_data类不应该定义两种类型转换运算符.因为对于该类来讲.它包含三个数据成员:bookNo,units_sold,和revenue,只有三者在一起才是有效的数据
但是如果确实想要定义这两个类型转换运算符的话.应该把它们声明成explicit的.这样可以防止Sales_data在某些情况下被默认转换成string或double类型.这有可能导致我们意料之外的运算结果.
14.47
前者将对象转换成const int.在接受const int值的地方才能够使用.后者将对象转换成int值.相对来讲更加通用一些.
14.48
我们写的是Data。就三个数据成员.
year.month和day.
可以为Data类提供一个bool类型的转换运算符.用来检查三个数据成员.是否都是有效值.(比如,month是否介于1~12之间.day是否超过了当月的天数)
bool 类型转换运算符应该声明为explicit的,因为我们是有意要在条件表达式中使用它的.
14.49
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}
};
return 1 <= month && month <= 12 && 1 <= day && day <= days_per_month[isLeapYear() ? 1 : 0][month - 1];
}
bool isLeapYear() {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
14.50
struct LongDouble {
LongDouble(double = 0.0);
operator double();
operator float();
};
LongDouble ldObj;
int ex1 = ldObj;
float ex2 = ldObj;
对于int ex1=ldObj;它需要把LongDouble类型转换成int类型.但是LongDouble并没有定义对应的类型转换运算符.因此它会尝试其他的来进行转换,其中operator double()和operator float()都满足需求.但编译器无法确定哪一个更合适.因此非产生二义性错误.conversion from
‘Longdouble’ to ‘int’ is ambiguous
对于float ex2=ldObj;它需要把LongDouble转换成float类型,而我们恰好定义了对应的类型转换运算符.因此只需要直接调用operator float()即可.
14.51
这里会优先调用void calc(int)函数.因为double 转换为int是标准类型转换.而转换为LongDouble则是为用户自定义类型.实际是调用了转换构造函数,前者优先.