C++Primer_课后习题第十四章

本文答案,部分参考于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则是为用户自定义类型.实际是调用了转换构造函数,前者优先.

14.52+14.53

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值