C++ Primer第5版 第十四章课后练习答案
合集《C++ Primer第5版》 课后练习答案 - 丸子球球 - 博客园 (cnblogs.com)
练习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;
}
练习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;
}
练习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]; };
练习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>()},
{```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; }
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
练习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的转换。
练习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;
练习14.53
SmallInt s1;
不合法,因为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;
}