第十四章 重载运算与类型转换

练习14.1

在什么情况下重载的运算符与内置运算符有所区别?在什么情况下重载的运算符又与内置运算符一样?

解:

不同点

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

相同点

  • 对于优先级、结合性和操作数的数目都不变
    答案还说了一个不同点,重载操作符必须具有至少一个class、struct和枚举类型的操作数
    但是经过笔者实测,好像不对
#include <iostream>using namespace std;struct Test
{int operator()(bool b, int iA, int iB) {return b ? iA : iB;}
};int main()
{cout << Test ()(1, 2, 3) << endl;return 0;
}

练习14.2

Sales_data 编写重载的输入、输出、加法和复合赋值运算符。

解:

头文件:

#include <string>
#include <iostream>class Sales_data {friend std::istream& operator>>(std::istream&, Sales_data&); // inputfriend std::ostream& operator<<(std::ostream&, const Sales_data&); // outputfriend Sales_data operator+(const Sales_data&, const Sales_data&); // additionpublic:Sales_data(const std::string &s, unsigned n, double p):bookNo(s), units_sold(n), revenue(n*p){ }Sales_data() : Sales_data("", 0, 0.0f){ }Sales_data(const std::string &s) : Sales_data(s, 0, 0.0f){ }Sales_data(std::istream &is);Sales_data& operator+=(const Sales_data&); // compound-assignmentstd::string isbn() const { return bookNo; }private:inline double avg_price() const;std::string bookNo;unsigned units_sold = 0;double revenue = 0.0;
};std::istream& operator>>(std::istream&, Sales_data&);
std::ostream& operator<<(std::ostream&, const Sales_data&);
Sales_data operator+(const Sales_data&, const Sales_data&);inline double Sales_data::avg_price() const
{return units_sold ? revenue/units_sold : 0;
}

主函数:

#include "ex_14_02.h"Sales_data::Sales_data(std::istream &is) : Sales_data()
{is >> *this;
}Sales_data& Sales_data::operator+=(const Sales_data &rhs)
{units_sold += rhs.units_sold;revenue += rhs.revenue;return *this;
}std::istream& operator>>(std::istream &is, Sales_data &item)
{double price = 0.0;is >> item.bookNo >> item.units_sold >> price;if (is)item.revenue = price * item.units_sold;elseitem = Sales_data();return is;
}std::ostream& operator<<(std::ostream &os, const Sales_data &item)
{os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price();return os;
}Sales_data operator+(const Sales_data &lhs, const Sales_data &rhs)
{Sales_data sum = lhs;sum += rhs;return sum;
}

练习14.3

stringvector 都定义了重载的==以比较各自的对象,假设 svec1svec2 是存放 stringvector,确定在下面的表达式中分别使用了哪个版本的==

(a) "cobble" == "stone"
(b) svec1[0] == svec2[0]
(c) svec1 == svec2
(d) svec1[0] == "stone"

解:

  • (a) 应用了c++内置版本的==,比较的是两个指针
  • (b) string
  • © vector
  • (d) string,字符串字面常量被转换成string

练习14.4

如何确定下列运算符是否应该是类的成员?

(a) %
(b) %=
(c) ++
(d) ->
(e) <<
(f) &&
(g) ==
(h) ()

解:
本题笔者也不太懂,仁者见仁智者见智吧。。。

  • (a) 不需要是成员。
  • (b) 是成员。
  • © 是成员。
  • (d) 必须是成员。
  • (e) 不需要是成员。
  • (f) 不需要是成员。
  • (g) 不需要是成员。
  • (h) 必须是成员。

练习14.5

在7.5.1节中的练习7.40中,编写了下列类中某一个的框架,请问在这个类中应该定义重载的运算符吗?如果是,请写出来。

(a) Book
(b) Date
(c) Employee
(d) Vehicle
(e) Object
(f) Tree

解:

Book,应该重载。

头文件:

#include <iostream>
#include <string>class Book
{friend std::istream& operator>>(std::istream&, Book&);friend std::ostream& operator<<(std::ostream&, const Book&);friend bool operator==(const Book&, const Book&);friend bool operator!=(const Book&, const Book&);public:Book() = default;Book(unsigned no, std::string name, std::string author, std::string pubdate) :no_(no), name_(name), author_(author), pubdate_(pubdate) {}Book(std::istream &in) { in >> *this; }private:unsigned no_;std::string name_;std::string author_;std::string pubdate_;
};std::istream& operator>>(std::istream&, Book&);
std::ostream& operator<<(std::ostream&, const Book&);
bool operator==(const Book&, const Book&);
bool operator!=(const Book&, const Book&);

实现:

#include "ex_14_5.h"std::istream& operator>>(std::istream &in, Book &book)
{in >> book.no_ >> book.name_ >> book.author_ >> book.pubdate_;if (!in)book = Book();return in;
}std::ostream& operator<<(std::ostream &out, const Book &book)
{out << book.no_ << " " << book.name_ << " " << book.author_ << " " << book.pubdate_;return out;
}bool operator==(const Book &lhs, const Book &rhs)
{return lhs.no_ == rhs.no_;
}bool operator!=(const Book &lhs, const Book &rhs)
{return !(lhs == rhs);
}

测试:

#include "ex_14_5.h"int main()
{Book book1(123, "CP5", "Lippman", "2012");Book book2(123, "CP5", "Lippman", "2012");if (book1 == book2)std::cout << book1 << std::endl;
}

练习14.6

为你的 Sales_data 类定义输出运算符。

解:

参考14.2。

练习14.7

你在13.5节的练习中曾经编写了一个String类,为它定义一个输出运算符。

解:

头文件:

#include <memory>
#include <iostream>class String
{friend std::ostream& operator<<(std::ostream&, const String&);
public:String() : String("") {}String(const char *);String(const String&);String& operator=(const String&);~String();const char *c_str() const { return elements; }size_t size() const { return end - elements; }size_t length() const { return end - elements - 1; }private:std::pair<char*, char*> alloc_n_copy(const char*, const char*);void range_initializer(const char*, const char*);void free();private:char *elements;char *end;std::allocator<char> alloc;
};std::ostream& operator<<(std::ostream&, const String&);

实现:

#include "ex_14_7.h"
#include <algorithm>
#include <iostream>std::pair<char*, char*>
String::alloc_n_copy(const char *b, const char *e)
{auto str = alloc.allocate(e - b);return{ str, std::uninitialized_copy(b, e, str) };
}void String::range_initializer(const char *first, const char *last)
{auto newstr = alloc_n_copy(first, last);elements = newstr.first;end = newstr.second;
}String::String(const char *s)
{char *sl = const_cast<char*>(s);while (*sl)++sl;range_initializer(s, ++sl);
}String::String(const String& rhs)
{range_initializer(rhs.elements, rhs.end);std::cout << "copy constructor" << std::endl;
}void String::free()
{if (elements){std::for_each(elements, end, [this](char &c) { alloc.destroy(&c); });alloc.deallocate(elements, end - elements);}
}String::~String()
{free();
}String& String::operator = (const String &rhs)
{auto newstr = alloc_n_copy(rhs.elements, rhs.end);free();elements = newstr.first;end = newstr.second;std::cout << "copy-assignment" << std::endl;return *this;
}std::ostream& operator<<(std::ostream &os, const String &s)
{char *c = const_cast<char*>(s.c_str());while (*c)os << *c++;return os;
}

测试:

#include "ex_14_7.h"int main()
{String str("Hello World");std::cout << str << std::endl;
}

练习14.8

你在7.5.1节中的练习中曾经选择并编写了一个类,为它定义一个输出运算符。

解:

参考14.5。

练习14.9

为你的 Sales_data 类定义输入运算符。

解:
参考14.2。

练习14.10

对于 Sales_data 的输入运算符来说如果给定了下面的输入将发生什么情况?

(a) 0-201-99999-9 10 24.95
(b) 10 24.95 0-210-99999-9

解:

  • (a) 格式正确。
  • (b) 不合法的输入。因为程序试图将 0-210-99999-9 转换为 float

练习14.11

下面的 Sales_data 输入运算符存在错误吗?如果有,请指出来。对于这个输入运算符如果仍然给定上个练习的输入将会发生什么情况?

istream& operator>>(istream& in, Sales_data& s)
{double price;in >> s.bookNo >> s.units_sold >> price;s.revence = s.units_sold >> price;return in;
}

解:

没有输入检查,什么也不会发生。

练习14.12

你在7.5.1节的练习中曾经选择并编写了一个类,为它定义一个输入运算符并确保该运算符可以处理输入错误。

解:

参考14.5。

练习14.13

你认为 Sales_data 类还应该支持哪些其他算术运算符?如果有的话,请给出它们的定义。

解:

没有其他了。

练习14.14

你觉得为什么调用 operator+= 来定义operator+ 比其他方法更有效?

解:

因为用 operator+= 会避免使用一个临时对象,而使得更有效。

练习14.15

你在7.5.1节的练习7.40中曾经选择并编写了一个类,你认为它应该含有其他算术运算符吗?如果是,请实现它们;如果不是,解释原因。

解:

头文件:

#include <iostream>
#include <string>class Book
{friend std::istream& operator>>(std::istream&, Book&);friend std::ostream& operator<<(std::ostream&, const Book&);friend bool operator==(const Book&, const Book&);friend bool operator!=(const Book&, const Book&);friend bool operator<(const Book&, const Book&);friend bool operator>(const Book&, const Book&);friend Book operator+(const Book&, const Book&);public:Book() = default;Book(unsigned no, std::string name, std::string author, std::string pubdate, unsigned number) :no_(no), name_(name), author_(author), pubdate_(pubdate), number_(number) {}Book(std::istream &in) { in >> *this; }Book& operator+=(const Book&);private:unsigned no_;std::string name_;std::string author_;std::string pubdate_;unsigned number_;
};std::istream& operator>>(std::istream&, Book&);
std::ostream& operator<<(std::ostream&, const Book&);
bool operator==(const Book&, const Book&);
bool operator!=(const Book&, const Book&);
bool operator<(const Book&, const Book&);
bool operator>(const Book&, const Book&);
Book operator+(const Book&, const Book&);

实现:

#include "ex_14_15.h"std::istream& operator>>(std::istream &in, Book &book)
{in >> book.no_ >> book.name_ >> book.author_ >> book.pubdate_ >> book.number_;return in;
}std::ostream& operator<<(std::ostream &out, const Book &book)
{out << book.no_ << " " << book.name_ << " " << book.author_ << " " << book.pubdate_ << " " << book.number_ << std::endl;return out;
}bool operator==(const Book &lhs, const Book &rhs)
{return lhs.no_ == rhs.no_;
}bool operator!=(const Book &lhs, const Book &rhs)
{return !(lhs == rhs);
}bool operator<(const Book &lhs, const Book &rhs)
{return lhs.no_ < rhs.no_;
}bool operator>(const Book &lhs, const Book &rhs)
{return rhs < lhs;
}Book& Book::operator+=(const Book &rhs)
{if (rhs == *this)this->number_ += rhs.number_;return *this;
}Book operator+(const Book &lhs, const Book &rhs)
{Book book = lhs;book += rhs;return book;
}

测试:

#include "ex_14_15.h"int main()
{Book cp5_1(12345, "CP5", "Lippmen", "2012", 2);Book cp5_2(12345, "CP5", "Lippmen", "2012", 4);std::cout << cp5_1 + cp5_2 << std::endl;
}

练习14.16

为你的 StrBlob 类、StrBlobPtr 类、StrVec 类和 String 类分别定义相等运算符和不相等运算符。

解:

练习14.17

你在7.5.1节中的练习7.40中曾经选择并编写了一个类,你认为它应该含有相等运算符吗?如果是,请实现它;如果不是,解释原因。

解:

参考14.15。

练习14.18

为你的 StrBlob 类、StrBlobPtr 类、StrVec 类和 String 类分别定义关系运算符。

解:

练习14.19

你在7.5.1节的练习7.40中曾经选择并编写了一个类,你认为它应该含有关系运算符吗?如果是,请实现它;如果不是,解释原因。

解:

参考14.15。

练习14.20

为你的 Sales_data 类定义加法和复合赋值运算符。

解:

参考14.2。

练习14.21

编写 Sales_data 类的++= 运算符,使得 + 执行实际的加法操作而 += 调用+。相比14.3节和14.4节对这两个运算符的定义,本题的定义有何缺点?试讨论之。

解:

缺点:使用了一个 Sales_data 的临时对象,但它并不是必须的。

练习14.22

定义赋值运算符的一个新版本,使得我们能把一个表示 ISBNstring 赋给一个 Sales_data 对象。

解:

头文件:

#include <string>
#include <iostream>class Sales_data
{friend std::istream& operator>>(std::istream&, Sales_data&);friend std::ostream& operator<<(std::ostream&, const Sales_data&);friend Sales_data operator+(const Sales_data&, const Sales_data&);public:Sales_data(const std::string &s, unsigned n, double p) :bookNo(s), units_sold(n), revenue(n*p) {}Sales_data() : Sales_data("", 0, 0.0f) {}Sales_data(const std::string &s) : Sales_data(s, 0, 0.0f) {}Sales_data(std::istream &is);Sales_data& operator=(const std::string&);Sales_data& operator+=(const Sales_data&);std::string isbn() const { return bookNo; }private:inline double avg_price() const;std::string bookNo;unsigned units_sold = 0;double revenue = 0.0;
};std::istream& operator>>(std::istream&, Sales_data&);
std::ostream& operator<<(std::ostream&, const Sales_data&);
Sales_data operator+(const Sales_data&, const Sales_data&);inline double Sales_data::avg_price() const
{return units_sold ? revenue / units_sold : 0;
}

实现:

#include "ex_14_22.h"Sales_data::Sales_data(std::istream &is) : Sales_data()
{is >> *this;
}Sales_data& Sales_data::operator+=(const Sales_data &rhs)
{units_sold += rhs.units_sold;revenue += rhs.revenue;return *this;
}std::istream& operator>>(std::istream &is, Sales_data &item)
{double price = 0.0;is >> item.bookNo >> item.units_sold >> price;if (is)item.revenue = price * item.units_sold;elseitem = Sales_data();return is;
}std::ostream& operator<<(std::ostream &os, const Sales_data &item)
{os << item.isbn() << " " << item.units_sold << " " << item.revenue << " " << item.avg_price();return os;
}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 std::string &isbn)
{*this = Sales_data(isbn);return *this;
}

测试:

#include "ex_4_22.h"int main()
{std::string strCp5("C++ Primer 5th");Sales_data cp5 = strCp5;std::cout << cp5 << std::endl;
}

练习14.23

为你的StrVec 类定义一个 initializer_list 赋值运算符。

解:

头文件:

#include <memory>
#include <string>
#include <initializer_list>#ifndef _MSC_VER
#define NOEXCEPT noexcept
#else
#define NOEXCEPT
#endifclass StrVec
{friend bool operator==(const StrVec&, const StrVec&);friend bool operator!=(const StrVec&, const StrVec&);friend bool operator< (const StrVec&, const StrVec&);friend bool operator> (const StrVec&, const StrVec&);friend bool operator<=(const StrVec&, const StrVec&);friend bool operator>=(const StrVec&, const StrVec&);public:StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {}StrVec(std::initializer_list<std::string>);StrVec(const StrVec&);StrVec& operator=(const StrVec&);StrVec(StrVec&&) NOEXCEPT;StrVec& operator=(StrVec&&)NOEXCEPT;~StrVec();StrVec& operator=(std::initializer_list<std::string>);void push_back(const std::string&);size_t size() const { return first_free - elements; }size_t capacity() const { return cap - elements; }std::string *begin() const { return elements; }std::string *end() const { return first_free; }std::string& at(size_t pos) { return *(elements + pos); }const std::string& at(size_t pos) const { return *(elements + pos); }void reserve(size_t new_cap);void resize(size_t count);void resize(size_t count, const std::string&);private:std::pair<std::string*, std::string*> alloc_n_copy(const std::string*, const std::string*);void free();void chk_n_alloc() { if (size() == capacity()) reallocate(); }void reallocate();void alloc_n_move(size_t new_cap);void range_initialize(const std::string*, const std::string*);private:std::string *elements;std::string *first_free;std::string *cap;std::allocator<std::string> alloc;
};bool operator==(const StrVec&, const StrVec&);
bool operator!=(const StrVec&, const StrVec&);
bool operator< (const StrVec&, const StrVec&);
bool operator> (const StrVec&, const StrVec&);
bool operator<=(const StrVec&, const StrVec&);
bool operator>=(const StrVec&, const StrVec&);

实现:

#include "ex_14_23.h"
#include <algorithm>void StrVec::push_back(const std::string &s)
{chk_n_alloc();alloc.construct(first_free++, s);
}std::pair<std::string*, std::string*>
StrVec::alloc_n_copy(const std::string *b, const std::string *e)
{auto data = alloc.allocate(e - b);return{ data, std::uninitialized_copy(b, e, data) };
}void StrVec::free()
{if (elements){for_each(elements, first_free, [this](std::string &rhs) { alloc.destroy(&rhs); });alloc.deallocate(elements, cap - elements);}
}void StrVec::range_initialize(const std::string *first, const std::string *last)
{auto newdata = alloc_n_copy(first, last);elements = newdata.first;first_free = cap = newdata.second;
}StrVec::StrVec(const StrVec &rhs)
{range_initialize(rhs.begin(), rhs.end());
}StrVec::StrVec(std::initializer_list<std::string> il)
{range_initialize(il.begin(), il.end());
}StrVec::~StrVec()
{free();
}StrVec& StrVec::operator = (const StrVec &rhs)
{auto data = alloc_n_copy(rhs.begin(), rhs.end());free();elements = data.first;first_free = cap = data.second;return *this;
}void StrVec::alloc_n_move(size_t new_cap)
{auto newdata = alloc.allocate(new_cap);auto dest = newdata;auto elem = elements;for (size_t i = 0; i != size(); ++i)alloc.construct(dest++, std::move(*elem++));free();elements = newdata;first_free = dest;cap = elements + new_cap;
}void StrVec::reallocate()
{auto newcapacity = size() ? 2 * size() : 1;alloc_n_move(newcapacity);
}void StrVec::reserve(size_t new_cap)
{if (new_cap <= capacity()) return;alloc_n_move(new_cap);
}void StrVec::resize(size_t count)
{resize(count, std::string());
}void StrVec::resize(size_t count, const std::string &s)
{if (count > size()){if (count > capacity()) reserve(count * 2);for (size_t i = size(); i != count; ++i)alloc.construct(first_free++, s);}else if (count < size()){while (first_free != elements + count)alloc.destroy(--first_free);}
}StrVec::StrVec(StrVec &&s) NOEXCEPT : elements(s.elements), first_free(s.first_free), cap(s.cap)
{// leave s in a state in which it is safe to run the destructor.s.elements = s.first_free = s.cap = nullptr;
}StrVec& StrVec::operator = (StrVec &&rhs) NOEXCEPT
{if (this != &rhs){free();elements = rhs.elements;first_free = rhs.first_free;cap = rhs.cap;rhs.elements = rhs.first_free = rhs.cap = nullptr;}return *this;
}bool operator==(const StrVec &lhs, const StrVec &rhs)
{return (lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()));
}bool operator!=(const StrVec &lhs, const StrVec &rhs)
{return !(lhs == rhs);
}bool operator<(const StrVec &lhs, const StrVec &rhs)
{return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}bool operator>(const StrVec &lhs, const StrVec &rhs)
{return rhs < lhs;
}bool operator<=(const StrVec &lhs, const StrVec &rhs)
{return !(rhs < lhs);
}bool operator>=(const StrVec &lhs, const StrVec &rhs)
{return !(lhs < rhs);
}StrVec& StrVec::operator=(std::initializer_list<std::string> il)
{*this = StrVec(il);return *this;
}

测试:

#include "ex_14_23.h"
#include <vector>
#include <iostream>int main()
{StrVec vec;vec.reserve(6);std::cout << "capacity(reserve to 6): " << vec.capacity() << std::endl;vec.reserve(4);std::cout << "capacity(reserve to 4): " << vec.capacity() << std::endl;vec.push_back("hello");vec.push_back("world");vec.resize(4);for (auto i = vec.begin(); i != vec.end(); ++i)std::cout << *i << std::endl;std::cout << "-EOF-" << std::endl;vec.resize(1);for (auto i = vec.begin(); i != vec.end(); ++i)std::cout << *i << std::endl;std::cout << "-EOF-" << std::endl;StrVec vec_list{ "hello", "world", "pezy" };for (auto i = vec_list.begin(); i != vec_list.end(); ++i)std::cout << *i << " ";std::cout << std::endl;// Test operator==const StrVec const_vec_list = { "hello", "world", "pezy" };if (vec_list == const_vec_list)for (const auto &str : const_vec_list)std::cout << str << " ";std::cout << std::endl;// Test operator<const StrVec const_vec_list_small = { "hello", "pezy", "ok" };std::cout << (const_vec_list_small < const_vec_list) << std::endl;
}

练习14.24

你在7.5.1节的练习7.40中曾经选择并编写了一个类,你认为它应该含有拷贝赋值和移动赋值运算符吗?如果是,请实现它们。

解:

头文件:

#ifndef DATE_H
#define DATE_H#ifndef _MSC_VER
#define NOEXCEPT noexcept
#else
#define NOEXCEPT
#endif#include <iostream>
#include <vector>class Date
{friend  bool            operator ==(const Date& lhs, const Date& rhs);friend  bool            operator < (const Date &lhs, const Date &rhs);friend  bool            check(const Date &d);friend  std::ostream&   operator <<(std::ostream& os, const Date& d);
public:typedef std::size_t Size;// default constructorDate() = default;// constructor taking Size as daysexplicit Date(Size days);// constructor taking three SizeDate(Size d, Size m, Size y) : day(d), month(m), year(y) {}// constructor taking iostreamDate(std::istream &is, std::ostream &os);// copy constructorDate(const Date& d);// move constructorDate(Date&& d) NOEXCEPT;// copy operator=Date& operator= (const Date& d);// move operator=Date& operator= (Date&& rhs) NOEXCEPT;// destructor  --  in this case, user-defined destructor is not nessary.~Date() { std::cout << "destroying\n"; }// membersSize toDays() const;  //not implemented yet.Date& operator +=(Size offset);Date& operator -=(Size offset);private:Size    day = 1;Size    month = 1;Size    year = 0;
};static const Date::Size YtoD_400 = 146097;    //365*400 + 400/4 -3 == 146097
static const Date::Size YtoD_100 = 36524;    //365*100 + 100/4 -1 ==  36524
static const Date::Size YtoD_4 = 1461;    //365*4 + 1          ==   1461
static const Date::Size YtoD_1 = 365;    //365// normal year
static const std::vector<Date::Size> monthsVec_n =
{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };// leap year
static const std::vector<Date::Size> monthsVec_l =
{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };// non-member operators:  <<  >>  -   ==  !=  <   <=  >   >=
//
std::ostream&
operator <<(std::ostream& os, const Date& d);
std::istream&
operator >>(std::istream& is, Date& d);
int
operator - (const Date& lhs, const Date& rhs);
bool
operator ==(const Date& lhs, const Date& rhs);
bool
operator !=(const Date& lhs, const Date& rhs);
bool
operator < (const Date& lhs, const Date& rhs);
bool
operator <=(const Date& lhs, const Date& rhs);
bool
operator  >(const Date& lhs, const Date& rhs);
bool
operator >=(const Date& lhs, const Date& rhs);
Date
operator - (const Date& lhs, Date::Size  rhs);
Date
operator  +(const Date& lhs, Date::Size  rhs);//  utillities:
bool check(const Date &d);
inline bool
isLeapYear(Date::Size y);// check if the date object passed in is valid
inline bool
check(const Date &d)
{if (d.month == 0 || d.month >12)return false;else{//    month == 1 3 5 7 8 10 12if (d.month == 1 || d.month == 3 || d.month == 5 || d.month == 7 ||d.month == 8 || d.month == 10 || d.month == 12){if (d.day == 0 || d.day > 31) return false;elsereturn true;}else{//    month == 4 6 9 11if (d.month == 4 || d.month == 6 || d.month == 9 || d.month == 11){if (d.day == 0 || d.day > 30) return false;elsereturn true;}else{//    month == 2if (isLeapYear(d.year)){if (d.day == 0 || d.day >29)  return false;elsereturn true;}else{if (d.day == 0 || d.day >28)  return false;elsereturn true;}}}}
}inline bool
isLeapYear(Date::Size y)
{if (!(y % 400)){return true;}else{if (!(y % 100)){return false;}elsereturn !(y % 4);}
}
#endif // DATE_H

实现:

#include "ex_14_24.h"
#include <algorithm>// constructor taking Size as days
// the argument must be within (0, 2^32)
Date::Date(Size days)
{// calculate the yearSize y400 = days / YtoD_400;Size y100 = (days - y400*YtoD_400) / YtoD_100;Size y4 = (days - y400*YtoD_400 - y100*YtoD_100) / YtoD_4;Size y = (days - y400*YtoD_400 - y100*YtoD_100 - y4*YtoD_4) / 365;Size d = days - y400*YtoD_400 - y100*YtoD_100 - y4*YtoD_4 - y * 365;this->year = y400 * 400 + y100 * 100 + y4 * 4 + y;// check if leap and choose the months vector accordinglystd::vector<Size>currYear= isLeapYear(this->year) ? monthsVec_l : monthsVec_n;// calculate day and month using find_if + lambdaSize D_accumu = 0, M_accumu = 0;// @bug    fixed:  the variabbles above hade been declared inside the find_if as static//                 which caused the bug. It works fine now after being move outside.std::find_if(currYear.cbegin(), currYear.cend(), [&](Size m){D_accumu += m;M_accumu++;if (d < D_accumu){this->month = M_accumu;this->day = d + m - D_accumu;return true;}elsereturn false;});
}// construcotr taking iostream
Date::Date(std::istream &is, std::ostream &os)
{is >> day >> month >> year;if (is){if (check(*this)) return;else{os << "Invalid input! Object is default initialized.";*this = Date();}}else{os << "Invalid input! Object is default initialized.";*this = Date();}}// copy constructor
Date::Date(const Date &d) :
day(d.day), month(d.month), year(d.year)
{}// move constructor
Date::Date(Date&& d) NOEXCEPT :
day(d.day), month(d.month), year(d.year)
{ std::cout << "copy moving"; }// copy operator=
Date &Date::operator= (const Date &d)
{this->day = d.day;this->month = d.month;this->year = d.year;return *this;
}// move operator=
Date &Date::operator =(Date&& rhs) NOEXCEPT
{if (this != &rhs){this->day = rhs.day;this->month = rhs.month;this->year = rhs.year;}std::cout << "moving =";return *this;
}// conver to days
Date::Size Date::toDays() const
{Size result = this->day;// check if leap and choose the months vector accordinglystd::vector<Size>currYear= isLeapYear(this->year) ? monthsVec_l : monthsVec_n;// calculate result + days by monthsfor (auto it = currYear.cbegin(); it != currYear.cbegin() + this->month - 1; ++it)result += *it;// calculate result + days by yearsresult += (this->year / 400)      * YtoD_400;result += (this->year % 400 / 100)  * YtoD_100;result += (this->year % 100 / 4)    * YtoD_4;result += (this->year % 4)        * YtoD_1;return result;
}// member operators:   +=  -=Date &Date::operator +=(Date::Size offset)
{*this = Date(this->toDays() + offset);return *this;
}Date &Date::operator -=(Date::Size offset)
{if (this->toDays() > offset)*this = Date(this->toDays() - offset);else*this = Date();return *this;
}// non-member operators:  <<  >>  -   ==  !=  <   <=  >   >=std::ostream&
operator <<(std::ostream& os, const Date& d)
{os << d.day << " " << d.month << " " << d.year;return os;
}std::istream&
operator >>(std::istream& is, Date& d)
{if (is){Date input = Date(is, std::cout);if (check(input))    d = input;}return is;
}int operator -(const Date &lhs, const Date &rhs)
{return lhs.toDays() - rhs.toDays();
}bool operator ==(const Date &lhs, const Date &rhs)
{return (lhs.day == rhs.day) &&(lhs.month == rhs.month) &&(lhs.year == rhs.year);
}bool operator !=(const Date &lhs, const Date &rhs)
{return !(lhs == rhs);
}bool operator < (const Date &lhs, const Date &rhs)
{return lhs.toDays() < rhs.toDays();
}bool operator <=(const Date &lhs, const Date &rhs)
{return (lhs < rhs) || (lhs == rhs);
}bool operator >(const Date &lhs, const Date &rhs)
{return !(lhs <= rhs);
}bool operator >=(const Date &lhs, const Date &rhs)
{return !(lhs < rhs);
}Date operator - (const Date &lhs, Date::Size rhs)
{                                       //  ^^^ rhs must not be larger than 2^32-1// copy lhsDate result(lhs);result -= rhs;return result;
}Date operator + (const Date &lhs, Date::Size rhs)
{                                       //  ^^^ rhs must not be larger than 2^32-1// copy lhsDate result(lhs);result += rhs;return result;
}

测试:

#include "ex_14_24.h"
#include <iostream>int main()
{Date lhs(9999999), rhs(1);std::cout << (lhs -= 12000) << "\n";return 0;
}

练习14.25

上题的这个类还需要定义其他赋值运算符吗?如果是,请实现它们;同时说明运算对象应该是什么类型并解释原因。

解:

是。如上题。

练习14.26

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

解:

练习14.27

为你的 StrBlobPtr 类添加递增和递减运算符。

解:

只显示添加的代码:

class StrBlobPtr {public:string& deref() const;StrBlobPtr& operator++();StrBlobPtr& operator--();StrBlobPtr operator++(int);StrBlobPtr operator--(int);StrBlobPtr& operator+=(size_t);StrBlobPtr& operator-=(size_t);StrBlobPtr operator+(size_t) const;StrBlobPtr operator-(size_t) const;
};inline StrBlobPtr& StrBlobPtr::operator++()
{check(curr, "increment past end of StrBlobPtr");++curr;return *this;
}inline StrBlobPtr& StrBlobPtr::operator--()
{check(--curr, "decrement past begin of StrBlobPtr");return *this;
}inline StrBlobPtr StrBlobPtr::operator++(int)
{StrBlobPtr ret = *this;++*this;return ret;
}inline StrBlobPtr StrBlobPtr::operator--(int)
{StrBlobPtr ret = *this;--*this;return ret;
}inline StrBlobPtr& StrBlobPtr::operator+=(size_t n)
{curr += n;check(curr, "increment past end of StrBlobPtr");return *this;
}inline StrBlobPtr& StrBlobPtr::operator-=(size_t n)
{curr -= n;check(curr, "increment past 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) const
{StrBlobPtr ret = *this;ret -= n;return ret;
}

练习14.28

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

解:

参考14.27。

练习14.29

为什么不定义const 版本的递增和递减运算符?

解:

因为递增和递减会改变对象本身,所以定义 const 版本的毫无意义。

练习14.30

为你的 StrBlobPtr 类和在12.1.6节练习12.22中定义的 ConstStrBlobPtr 的类分别添加解引用运算符和箭头运算符。注意:因为 ConstStrBlobPtr 的数据成员指向const vector,所以ConstStrBlobPtr 中的运算符必须返回常量引用。

解:

略。

练习14.31

我们的 StrBlobPtr 类没有定义拷贝构造函数、赋值运算符以及析构函数,为什么?

解:

因为使用合成的足够了。

练习14.32

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

解:

头文件:

class StrBlobPtr;class StrBlobPtr_pointer
{public:StrBlobPtr_pointer() = default;StrBlobPtr_pointer(StrBlobPtr* p) : pointer(p) { }StrBlobPtr& operator *() const;StrBlobPtr* operator->() const;private:StrBlobPtr* pointer = nullptr;
};

实现:

#include "ex_14_32.h"
#include "ex_14_30_StrBlob.h"
#include <iostream>StrBlobPtr&
StrBlobPtr_pointer::operator *() const
{return *pointer;
}StrBlobPtr*
StrBlobPtr_pointer::operator ->() const
{return pointer;
}

练习14.33

一个重载的函数调用运算符应该接受几个运算对象?

解:

0个或多个

练习14.34

定义一个函数对象类,令其执行if-then-else 的操作:该类的调用运算符接受三个形参,它首先检查第一个形参,如果成功返回第二个形参值;如果不成功返回第三个形参的值。

解:

struct Test
{int operator()(bool b, int iA, int iB) {return b ? iA : iB;}
};

练习14.35

编写一个类似于 PrintString 的类,令其从 istream 中读取一行输入,然后返回一个表示我们所读内容的string。如果读取失败,返回空string

解:

#include <iostream>
#include <string>class GetInput
{public:GetInput(std::istream &i = std::cin) : is(i) {}std::string operator()() const{std::string str;std::getline(is, str);return is ? str : std::string();}private:std::istream &is;
};int main()
{GetInput getInput;std::cout << getInput() << std::endl;
}

练习14.36

使用前一个练习定义的类读取标准输入,将每一行保存为 vector 的一个元素。

解:

#include <iostream>
#include <string>
#include <vector>class GetInput
{public:GetInput(std::istream &i = std::cin) : is(i) {}std::string operator()() const{std::string str;std::getline(is, str);return is ? str : std::string();}private:std::istream &is;
};int main()
{GetInput getInput;std::vector<std::string> vec;for (std::string tmp; !(tmp = getInput()).empty();) vec.push_back(tmp);for (const auto &str : vec) std::cout << str << " ";std::cout << std::endl;
}

练习14.37

编写一个类令其检查两个值是否相等。使用该对象及标准库算法编写程序,令其替换某个序列中具有给定值的所有实例。

解:

#include <iostream>
#include <algorithm>
#include <vector>class IsEqual
{int value;
public:IsEqual(int v) : value(v) {}bool operator()(int elem){return elem == value;}
};int main()
{std::vector<int> vec = { 3, 2, 1, 4, 3, 7, 8, 6 };std::replace_if(vec.begin(), vec.end(), IsEqual(3), 5);for (int i : vec) std::cout << i << " ";std::cout << std::endl;
}

练习14.38

编写一个类令其检查某个给定的 string 对象的长度是否与一个阈值相等。使用该对象编写程序,统计并报告在输入的文件中长度为1的单词有多少个,长度为2的单词有多少个、…、长度为10的单词有多少个。

解:

#include <iostream>
#include <string>
#include <fstream>
#include <vector>
#include <map>struct IsInRange
{IsInRange(std::size_t lower, std::size_t upper):_lower(lower), _upper(upper){}bool operator()(std::string const& str) const{return str.size() >= _lower && str.size() <= _upper;}std::size_t lower_limit() const{return _lower;}std::size_t upper_limit() const{return _upper;}
private:std::size_t _lower;std::size_t _upper;
};int main()
{//create predicates with various upper limits.std::size_t lower = 1;auto uppers = { 3u, 4u, 5u, 6u, 7u, 8u, 9u, 10u, 11u, 12u, 13u, 14u };std::vector<IsInRange> predicates;for (auto upper : uppers)predicates.push_back(IsInRange{ lower, upper });//create count_table to store counts std::map<std::size_t, std::size_t> count_table;for (auto upper : uppers)count_table[upper] = 0;//read file and countstd::ifstream fin("storyDataFile.txt");for (std::string word; fin >> word; /* */)for (auto is_size_in_range : predicates)if (is_size_in_range(word))++count_table[is_size_in_range.upper_limit()];//printfor (auto pair : count_table)std::cout << "count in range [1, " << pair.first << "] : " << pair.second << std::endl;return 0;
}

练习14.39

修改上一题的程序令其报告长度在1到9之间的单词有多少个、长度在10以上的单词有多少个。

解:

参考14.38。

练习14.40

重新编写10.3.2节的biggies 函数,使用函数对象替换其中的 lambda 表达式。

解:

#include <vector>
#include <string>
#include <iostream>
#include <algorithm>using namespace std;class ShorterString
{public:bool operator()(string const& s1, string const& s2) const { return s1.size() < s2.size(); }
};class BiggerEqual
{size_t sz_;
public:BiggerEqual(size_t sz) : sz_(sz) {}bool operator()(string const& s) { return s.size() >= sz_; }
};class Print
{public:void operator()(string const& s) { cout << s << " "; }
};string make_plural(size_t ctr, string const& word, string const& ending)
{return (ctr > 1) ? word + ending : word;
}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, vector<string>::size_type sz)
{elimDups(words);stable_sort(words.begin(), words.end(), ShorterString());auto wc = find_if(words.begin(), words.end(), BiggerEqual(sz));auto count = words.end() - wc;cout << count << " " << make_plural(count, "word", "s") << " of length " << sz << " or longer" << endl;for_each(wc, words.end(), Print());cout << endl;
}int main()
{vector<string> vec{ "fox", "jumps", "over", "quick", "red", "red", "slow", "the", "turtle" };biggies(vec, 4);
}

练习14.41

你认为 C++ 11 标准为什么要增加 lambda?对于你自己来说,什么情况下会使用 lambda,什么情况下会使用类?

解:

使用 lambda 是非常方便的,当需要使用一个函数,而这个函数不常使用并且简单时,使用lambda 是比较方便的选择。

练习14.42

使用标准库函数对象及适配器定义一条表达式,令其

(a) 统计大于1024的值有多少个。
(b) 找到第一个不等于pooh的字符串。
(c) 将所有的值乘以2。

解:

std::count_if(ivec.cbegin(), ivec.cend(), std::bind(std::greater<int>(), _1, 1024));
std::find_if(svec.cbegin(), svec.cend(), std::bind(std::not_equal_to<std::string>(), _1, "pooh"));
std::transform(ivec.begin(), ivec.end(), ivec.begin(), std::bind(std::multiplies<int>(), _1, 2));

练习14.43

使用标准库函数对象判断一个给定的int值是否能被 int 容器中的所有元素整除。

解:

#include <iostream>
#include <string>
#include <functional>
#include <algorithm>int main()
{auto data = { 2, 3, 4, 5 };int input;std::cin >> input;std::modulus<int> mod;auto predicator = [&](int i) { return 0 == mod(input, i); };auto is_divisible = std::any_of(data.begin(), data.end(), predicator);std::cout << (is_divisible ? "Yes!" : "No!") << std::endl;return 0;
}

练习14.44

编写一个简单的桌面计算器使其能处理二元运算。

解:

#include <iostream>
#include <string>
#include <map>
#include <functional> int add(int i, int j) { return i + j; }
auto mod = [](int i, int j) { return i % j; };
struct Div { int operator ()(int i, int j) const { return i / j; } };auto binops = std::map<std::string, std::function<int(int, int)>>
{{ "+", add },                               // function pointer { "-", std::minus<int>() },                 // library functor { "/", Div() },                             // user-defined functor { "*", [](int i, int j) { return i*j; } },  // unnamed lambda { "%", mod }                                // named lambda object
};int main()
{while (std::cout << "Pls enter as: num operator num :\n", true){int lhs, rhs; std::string op;std::cin >> lhs >> op >> rhs;std::cout << binops[op](lhs, rhs) << std::endl;}return 0;
}

练习14.45

编写类型转换运算符将一个 Sales_data 对象分别转换成 stringdouble,你认为这些运算符的返回值应该是什么?

解:

头文件:

#include <string>
#include <iostream>class Sales_data
{friend std::istream& operator>>(std::istream&, Sales_data&);friend std::ostream& operator<<(std::ostream&, const Sales_data&);friend Sales_data operator+(const Sales_data&, const Sales_data&);public:Sales_data(const std::string &s, unsigned n, double p) :bookNo(s), units_sold(n), revenue(n*p) {}Sales_data() : Sales_data("", 0, 0.0f) {}Sales_data(const std::string &s) : Sales_data(s, 0, 0.0f) {}Sales_data(std::istream &is);Sales_data& operator=(const std::string&);Sales_data& operator+=(const Sales_data&);explicit operator std::string() const { return bookNo; }explicit operator double() const { return avg_price(); }std::string isbn() const { return bookNo; }private:inline double avg_price() const;std::string bookNo;unsigned units_sold = 0;double revenue = 0.0;
};std::istream& operator>>(std::istream&, Sales_data&);
std::ostream& operator<<(std::ostream&, const Sales_data&);
Sales_data operator+(const Sales_data&, const Sales_data&);inline double Sales_data::avg_price() const
{return units_sold ? revenue / units_sold : 0;
}

练习14.46

你认为应该为 Sales_data 类定义上面两种类型转换运算符吗?应该把它们声明成 explicit 的吗?为什么?

解:

上面的两种类型转换有歧义,应该声明成 explicit 的。

练习14.47

说明下面这两个类型转换运算符的区别。

struct Integral {operator const int();operator int() const;
}

解:

第一个无意义,会被编译器忽略。第二个合法。

练习14.48

你在7.5.1节的练习7.40中曾经选择并编写了一个类,你认为它应该含有向 bool 的类型转换运算符吗?如果是,解释原因并说明该运算符是否应该是 explicit的;如果不是,也请解释原因。

解:

Date 类应该含有向 bool 的类型转换运算符,并且应该声明为 explicit 的。

练习14.49

为上一题提到的类定义一个转换目标是 bool 的类型转换运算符,先不用在意这么做是否应该。

解:

 explicit operator bool() { return (year<4000) ? true : false; }

练习14.50

在初始化 ex1ex2 的过程中,可能用到哪些类类型的转换序列呢?说明初始化是否正确并解释原因。

struct LongDouble {LongDouble(double = 0.0);operator double();operator float();
};
LongDouble ldObj;
int ex1 = ldObj;
float ex2 = ldObj;

解:

ex1 转换不合法,没有定义从 LongDoubleint 的转换,从double转换还是float转换存在二义性。ex2 合法。

练习14.51

在调用 calc 的过程中,可能用到哪些类型转换序列呢?说明最佳可行函数是如何被选出来的。

void calc(int);
void calc(LongDouble);double dval;
calc(dval);  // 调用了哪个calc?

解:

最佳可行函数是 void calc(int)

转换的优先级如下:

  1. 精确匹配
  2. const 转换。
  3. 类型提升
  4. 算术转换
  5. 类类型转换

练习14.52

在下面的加法表达式中分别选用了哪个operator+?列出候选函数、可行函数及为每个可行函数的实参执行的类型转换:

struct Longdouble {//用于演示的成员operator+;在通常情况下是个非成员LongDouble operator+(const SmallInt&);//其他成员与14.9.2节一致
};
LongDouble operator+(LongDouble&, double);
SmallInt si;
LongDouble ld;
ld = si + ld;
ld = ld + si;

解:

ld = si + ld; 不合法。ld = ld + si 两个都可以调用,但是第一个调用更精确一些。

练习14.53

假设我们已经定义了如第522页所示的SmallInt,判断下面的加法表达式是否合法。如果合法,使用了哪个加法运算符?如果不合法,应该怎样修改代码才能使其合法?

SmallInt si;
double d = si + 3.14;

解:

不合法,存在二义性。

应该该为:

SmallInt s1;
double d = s1 + SmallInt(3.14);

【c++ primer】第五版第十四章习题答案相关推荐

  1. C++Primer第五版 第十二章习题答案(1~10)

    1:知识点1:对象生命周期:全局对象在程序启动时分配,在程序结束时销毁.局部自动对象,当我们进入其定义所在程序块时被创建,在离开块时被销毁.局部static对象在第一次使用前分配,在程序结束时销毁 知 ...

  2. 【c++ primer】第五版第十六章习题答案

    练习16.1 给出实例化的定义. 解: 当编译器实例化一个模版时,它使用实际的模版参数代替对应的模版参数来创建出模版的一个新"实例". 练习16.2 编写并测试你自己版本的 com ...

  3. 【c++primer第五版】第十一章习题答案

    第十一章 关联容器 练习11.1 描述map和vector的不同. 解: 顺序容器中的元素是"顺序"存储的,对于vector,元素在其中按顺序存储,每个元素都有唯一对应 的位置编号 ...

  4. C Primer Plus(第6版)第十四章复习题答案

    1.正确的关键字是struct,最后一条声明语句以及结构模板有花括号要有分号. 2.输出如下 6 1 22 Spiffo Road S p 3.struct month { char name[10] ...

  5. C++Primer中文版(第4版)第四章习题答案

    习题4.1 假设get_size是一个没有参数并返回int值的函数,下列哪些定义是非法的?为什么? unsigned buf_size = 1024 (a) int ia[buf_size]; (b) ...

  6. 计算机网络原理 谢希仁(第8版)第四章习题答案

    4-01 网络层向上提供的服务有哪两种?试比较其优缺点. 面向连接的和无连接. 面向连接优点: 通过虚电路发送分组,分组只用填写虚电路编号,分组开销较小: 分组按序达到终点. 面向连接缺点: 一个节点 ...

  7. java程序设计基础_陈国君版第五版_第四章例题

    java程序设计基础_陈国君版第五版_第四章例题 <pre name="code" class="java">public class Main4_ ...

  8. 《汇编语言》王爽(第四版) 第十四章 检测点及实验14

    文章目录 目录 前言 一.检测点 1.检测点14.1 (1)检测点14.1.(1) (2)检测点14.1.(2) 2.检测点14.2 (1)实验任务 (2)实现代码 二.实验14 访问CMOS RAM ...

  9. 数据结构c语言版第三版实验四答案,数据结构(C语言版)第三四章习题答案

    Push( &s, t[i]); while( !EmptyStack( &s)) {// 每弹出一个字符与相应字符比较 temp=Pop (&s); if( temp!=S[ ...

最新文章

  1. 《iOS 全埋点技术白皮书》重磅推出
  2. 参会全攻略 | 倒计时 7 天!30+ 位重量级嘉宾“聊”什么?
  3. mybatis框架--学习笔记(上)
  4. 1.7 空间正交分解
  5. mysql数据库之运行时其他报错
  6. 扫二维码缴违章停车罚款?骗你的!
  7. zen brush 2 android,zen brush2
  8. 云呐|固定资产管理的目的,固定资产管理办法的目的
  9. 数据可视化8_数据分析的一般流程
  10. Redis Java客户端的选择
  11. 《用于自动越野导航的实时语义映射》论文笔记
  12. # 前后端国际化多语言配置
  13. 基于GEE的制作全球任意地方时间序列数据动画的方法
  14. 细说MySQL的时间戳(Timestamp)类型
  15. python使用蓝牙与HC05通讯(pybluez调用socket、模拟串口)
  16. DC/DC电路自举电容作用
  17. Scylladb 高可用测试结果
  18. 安卓手机可以运行linux程序吗,重磅!安卓系统也能运行PC程序了,实测效果令人惊在当场!...
  19. python 迭代器详解
  20. Android服务器 KSWEB v3.986 特别版

热门文章

  1. 基于微信小程序的学院通知与文件分享系统app设计与实现-计算机毕业设计源码+LW文档
  2. 陆奇天团二期成团,清北硕博超七成
  3. 大数据系列(一)之hadoop介绍及集群搭建
  4. 常用Linux性能检测命令
  5. google chrome
  6. 轻量级工具emoji-java处理emoji表情字符
  7. hive的分区和分桶
  8. 苹果CMS怎么更换模板详细教程
  9. 京东商城iOS客户端安装包瘦身实践
  10. Web前端全栈开发_node源码笔记【爱创课堂】