Exercise 13.1

1
2
3
4
5
6
7
8
// Within a class type, if the first parameter of one constructor is the reference of the class type itself, and left parameters all have default values, then this constrcutor is copy constructor.
// copy constructor is utilized in copy initialization:
// 1. Define variable using an =;
// 2. Pass an object as an argument to a parameter of noreference type;
// 3. Return an object from a function that has a nonreference return type;
// 4. Brace initialize the elements in an array or the members of an aggregate class;
// 5. Some class types also use copy initializetion for the objects they allocate.

Exercise 13.2

1
2
3
4
5
6
7
8
// Sales_data::Sales_data(Sales_data rhs)
// try to define a copy constructor of class type Sales_data;
// and the first parameter's type is Sales_data, it is illegal;
// when copy constructor is used in copy initialization, Sales_data object as argument is pass in to copy initialize the paramenter rhs;
// while this copy initialize will call the copy constructor itself;
// a infinite loop in the end;
// so the type of first parameter should be Sales_data&.

Exercise 13.3

1
2
3
// copy a StrBlob object would copy every non-static member of it, and StrBlob class type has a shared_ptr member, copy it will increase the reference count of the object which is pointed by shared_ptr.
// copy a StrBlob object would copy every non-static member of it, StrBlobPtr class type has a weak_ptr member, copy it will not influence the reference count of the object which is pointed by weak_ptr.

Exercise 13.4

1
2
3
4
5
6
7
8
9
Point global;
Point foo_var(Point arg)	// argument passed in would call copy constructor
{
  Point local = arg, *heap = new Point(global); // call copy constructor twice.
  *heap = local;				// copy-assignment operator
  Point pa[4] = {local, *heap};			// use curly braced list to initialize array would call copy constructor
  return *heap;					// return an object, and the return type of this function is not reference, then it calls copy constructor
}

Exercise 13.5

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class HasPtr
{
public:
  HasPtr(const string &s = string()):
    ps(new string(s)), i(0) {}
  HasPtr(const HasPtr &h): ps(new string(*h.ps)), i(h.i) {}
private:
  string *ps;
  int i;
};

Exercise 13.6

1
2
3
4
5
// copy-assignment operator defines what happened when assign an object to another object with same type.
// when assign an object to another object with same type, copy-assignment operator would be called.
// for sone class type, copy-assignment operator prevents the assignment of objects of this calss type, but mainly copy-assignment operator would assign every non-static member of right operand to the corresponding member of left operand using the copy-assignment operator for the type of the member.
// if there is no copy-assignment operator defined within a class type, compiler would generate a synthesized copy-assignment oeperator for it.

Exercise 13.7

1
2
3
// assign a StrBlob object to another StrBlob object would assign every non-static member of it, and StrBlob class type has a shared_ptr member, assign it will increase the reference count of the object which is pointed by right operand's shared_ptr, and decrease the reference count of the object which is pointed by the left operand's shared_ptr .
// assign a StrBlobPtr object to another StrBlobPtr object would assign every non-static member of it, StrBlobPtr class type has a weak_ptr member, assignment of it will not influence the reference count of the object which is pointed by weak_ptr.

Exercise 13.8

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class HasPtr
{
public:
  HasPtr(const string &s = std::string()):
    ps(new string(s)), i(0) {}
  HasPtr(const HasPtr &h): ps(new string), i(h.i) {}
  HasPtr &operator=(const HasPtr &);
private:
  string *ps;
  int i;
};
HasPtr& HasPtr::operator=(const HasPtr &h)
{
  auto newptr = new string(*h.ps);
  delete ps;
  ps = newptr;
  i = h.i;
  return *this;
}

Exercise 13.9

1
2
3
4
// A destructor is a member function of class type, its name is combined of ~ and class name, and no return value, no parameter.
// destructor releases the resource used by object, destruct every non-static member of object.
// compiler would generate a synthesized destructor for a class if it has not defined a destructor.

Exercise 13.10

1
2
3
// When a StrBlob object is destructed, every non-static member of it would be destructed, and descrease the reference count of the object which is pointed by object's share_ptr, if count comes to zero, that object be pointed is destructed, too.
// When StrBlobPtr object is destructed, every non-static member of it would be destructed, since it has a weak_ptr member, this ptr's destruction has no influence on the reference count of the object which is pointed by weak_ptr.

Exercise 13.11

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class HasPtr
{
public:
  HasPtr(const string &s = string()):
    ps(new string(s)), i(0) {}
  HasPtr(const HasPtr &h): ps(new string(*h.ps)), i(h.i) {}
  HasPtr &operator=(const HasPtr &);
  ~HasPtr() {delete ps;};
private:
  string *ps;
  int i;
};
HasPtr& HasPtr::operator=(const HasPtr &h)
{
  auto newptr = new string(*h.ps);
  delete ps;
  ps = newptr;
  i = h.i;
  return *this;
}

Exercise 13.12

1
2
3
4
5
6
7
8
9
bool fcn(const Sales_data *trans, Sales_data accum) // accum is copy initialized within this function body, it would be destructed when this function call ends.
{
  Sales_data item1(*trans), item2(accum); // both would be destructed when this function body ends
  return item1.isbn() != item2.isbn(); // the two string temp object would be destructed when the expression ends
  // and the temp bool object as result of this expression would be destructed after copy initialization of the return value.
}
// since destruction of Sales_data object would lead to the destruction of the string type member, and destruct string object would call its destructor.
// so there would be 2 + 2 + 2 + 1 + 1 = 8 times of calling of destructor.

Exercise 13.13

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
struct X
{
  X() {cout << "X()" << endl;}
  X(const X &) {cout << "X(const X &)" << endl;}
  X &operator=(const X &) {cout << "X &operator=(const X &x)" << endl;}
  ~X() {cout << "~X()" << endl;}
};
void test1(X &) {}
void test2(X) {}
int main()
{
  X x1;
  X x2 = x1;
  X *p = new X;
  test1(x1);
  test2(x1);
  X ax[] = {x1, x2, *p};
  delete p;
}

Exercise 13.14

1
2
3
// Using synthesized copy constructor
// the outputs are all the same.

Exercise 13.15

1
2
3
4
5
// if a copy constructor is defined within class type numbered,
// outputs would be changed, for that copy constructor would generage a new number for the object,
// what's more, function accepts a 'numbered s' type parameter, this would call copy instructor when called, and 's.mysn' is different from the argument passed in,
// different outputs, and not expected, do not match the real mysn member of original objects.

Exercise 13.16

1
2
3
4
// That would give the expected result.
// outputs would have three different serial numbers,
// and every one represents the real mysn member of the passed in numbered type object.

Exercise 13.17

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
struct numbered1
{
  int mysn;
};
struct numbered2
{
  int mysn;
  numbered2(const numbered2 &n): mysn(n.mysn + 1) {}
  numbered2(): mysn(0) {}
};
void f1(numbered1 s) {cout << s.mysn << endl;}
void f2(numbered2 s) {cout << s.mysn << endl;}
void f3(const numbered2 &s) {cout << s.mysn << endl;}
int main()
{
  // first
  numbered1 a1, b1 = a1, c1 = b1;
  f1(a1);
  f1(b1);
  f1(c1);
  // second
  numbered2 a2, b2 = a2, c2 = b2;
  f2(a2);
  f2(b2);
  f2(c2);
  // third
  f3(a2);
  f3(b2);
  f3(c2);
}

Exercise 13.18

1
2
// See Employee.h

Employee.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Employee
{
  static unsigned seed;
  string name;
  unsigned id = 0;
 public:
 Employee(): id(++seed) {}
 Employee(string n): name(n), id(++seed) {}
 Employee(const Employee &e): name(e.name), id(++seed) {}
  Employee &operator=(const Employee &e);
};
unsigned Employee::seed = 0;
Employee& Employee::operator=(const Employee &e)
{
  name = e.name;
  id = ++seed;
  return *this;
}

Exercise 13.19

1
2
3
4
// Should define copy constructor and copy assignment operator itself.
// For the name may be the same, but id of every employee object should be unique(id should be handled automatically)
// See Employee.h

Employee.h

Exercise 13.20

1
2
3
4
// copy: copy TextQuery or QueryResult object would copy every mamber of it, and would increase the reference count of the object which is pointed by the shared_ptr member;
// assignment: assign TextQuery or QueryResult object would assign every mamber of it, and would increase the reference count of the object which is pointed by the shared_ptr member, decrease the reference count of the object which is pointed by the original shared_ptr;
// destruct: destruct TextQuery or QueryResult object would destruct every mamber of it, and would decrease the reference count of the object which is pointed by the shared_ptr member;

Exercise 13.21

1
2
3
4
// Should define the copy constructor and copy assignment operator;
// copy objects of both types should just copy the content pointed by the share_ptr to another memory space pointed by the new shared_ptr, not directly copy the share_ptr object.
// See TextQuery.h

TextQuery.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#ifndef TEXTQUERY_H
#define TEXTQUERY_H
class QueryResult;
class TextQuery
{
 public:
  using line_no = vector<string>::size_type;
  TextQuery(ifstream &infile);
  TextQuery(const TextQuery &);
  TextQuery &operator=(const TextQuery &);
  QueryResult query(const string &s) const;
 private:
  friend QueryResult;
  shared_ptr<vector<string>> sp1;
  map<string, shared_ptr<set<line_no>>> mp;
};
TextQuery::TextQuery(ifstream &infile):
sp1(new vector<string>)
{
  line_no line_number = 0;
  string line;
  while(getline(infile, line))
    {
      // store line in vector<string>
      sp1->push_back(line);
      // scan through the line
      istringstream ist(line);
      string word;
      while(ist >> word)
      {
	if(ispunct(word[word.size() - 1]))
	  word.erase(word.size() - 1);
	auto &lines = mp[word];
	if(!lines) lines.reset(new set<line_no>);
	lines->insert(line_number);
      }
      // increase the line_number
      ++line_number;
    }
}
TextQuery::TextQuery(const TextQuery &t): sp1(make_shared<vector<string>>(*t.sp1)), mp(t.mp) {}
TextQuery &TextQuery::operator=(const TextQuery &t)
{
  sp1 = make_shared<vector<string>>(*t.sp1);
  mp = t.mp;
  return *this;
}
  class QueryResult
  {
    friend ostream &print(ostream &, const QueryResult &);
    string search_word;
    shared_ptr<vector<string>> sp1;
    shared_ptr<set<TextQuery::line_no>> sp2;
  public:
  QueryResult(const string &s,
	    shared_ptr<set<TextQuery::line_no>> p,
	    shared_ptr<vector<string>> file): search_word(s), sp1(file), sp2(p) {}
    QueryResult(const QueryResult &);
    QueryResult &operator=(const QueryResult &);
    set<TextQuery::line_no>::iterator begin() {return sp2->begin();}
    set<TextQuery::line_no>::iterator end() {return sp2->end();}
    shared_ptr<vector<string>> get_file() {return sp1;}
  };
QueryResult::QueryResult(const QueryResult &q): sp1(make_shared<vector<string>>(*q.sp1)), sp2(make_shared<set<TextQuery::line_no>>(*q.sp2)), search_word(q.search_word) {}
QueryResult &QueryResult::operator=(const QueryResult &q)
{
  sp1 = make_shared<vector<string>>(*q.sp1);
  sp2 = make_shared<set<TextQuery::line_no>>(*q.sp2);
  search_word = q.search_word;
  return *this;
}
  ostream &print(ostream &o, const QueryResult &q)
  {
    auto n = q.sp2->size();
    o << "element occurs " << n << " times" << endl;
    auto result = *q.sp2;
    for(const auto &l : result)
      cout << "\t(line " << l + 1 << ") "
	 << (*q.sp1)[l] << endl;
    return o;
  }
QueryResult TextQuery::query(const string &s) const
{
  static shared_ptr<set<line_no>> nodata(new set<line_no>);
  auto iter = mp.find(s);
  auto last = mp.end();
  if(iter != last)
    return QueryResult(s, iter->second, sp1);
  else
    return QueryResult(s, nodata, sp1);
}
#endif

Exercise 13.22

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class HasPtr
{
public:
  HasPtr(const string &s = string()):
    ps(new string(s)), i(0) {}
  HasPtr(const HasPtr &h): ps(new string(*h.ps)), i(h.i) {}
  HasPtr &operator=(const HasPtr &);
  ~HasPtr() {delete ps;};
private:
  string *ps;
  int i;
};
HasPtr& HasPtr::operator=(const HasPtr &h)
{
  auto newptr = new string(*h.ps);
  delete ps;
  ps = newptr;
  i = h.i;
  return *this;
}

Exercise 13.23

1
2
// See exercise 13.22.cpp, I made a lot of mistakes before.

13.22.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class HasPtr
{
public:
  HasPtr(const string &s = string()):
    ps(new string(s)), i(0) {}
  HasPtr(const HasPtr &h): ps(new string(*h.ps)), i(h.i) {}
  HasPtr &operator=(const HasPtr &);
  ~HasPtr() {delete ps;};
private:
  string *ps;
  int i;
};
HasPtr& HasPtr::operator=(const HasPtr &h)
{
  auto newptr = new string(*h.ps);
  delete ps;
  ps = newptr;
  i = h.i;
  return *this;
}

Exercise 13.24

1
2
3
// if not define a destructor in HasPtr class type, the dynamic memory space pointed by member ps would not be released after destruction of HasPtr object, then causes memory leak.
// if not define a copy constructor illustrated in section 12.2.1, copy initialization of HasPtr object would directly copy the value of member ps, thus at last two objects' ps member both point to the same dynamic memory space.

Exercise 13.25

1
2
3
4
5
6
// See StrBlob.h
// copy onstructor must allocate a new dynamic memory space for the content copied from the place pointed by original StrBlob object's shared_ptr, use the new shared_ptr object to initialize self's data member.
// copy assignment operator behaves almost like copy constructor, but it does not initialize data member, instead it assigns the new shared_ptr value to it.
// what's more, operator does not need to explicitly free the dynamic memory originally pointed by the old value of data member, for data's type is shared_ptr, the dynamic space arranged by shared_ptr would be automatically released when its reference count decreases to zero.
// StrBlob could rely its own synthesized destructor, for there is only one data member, and class type of member itself owns destrucor.

StrBlob.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#ifndef STRBLOB_H
#define STRBLOB_H
class StrBlobPtr;
class StrBlob
{
  friend class StrBlobPtr;
  friend class ConstStrBlobPtr;
  friend bool operator==(const StrBlob &, const StrBlob &);
  friend bool operator!=(const StrBlob &, const StrBlob &);
  friend bool operator<(const StrBlob &, const StrBlob &);
 public:
  typedef vector<string>::size_type size_type;
  StrBlob();
  StrBlob(initializer_list<string> il);
  StrBlob(const StrBlob &);
  StrBlob &operator=(const StrBlob &);
  string &operator[](size_t n);
  const string &operator[](size_t n) const;
  size_type size() const {return data->size();}
  bool empty() const {return data->empty();}
  void push_back(const string &t) {data->push_back(t);}
  void push_back(string &&t) {data->push_back(std::move(t));}
  void pop_back();
  string &front();
  const string &front() const;
  string &back();
  const string &back() const;
  StrBlobPtr begin();
    StrBlobPtr end();
 private:
  shared_ptr<vector<string>> data;
  void check(size_type i, const string &msg) const;
};
class StrBlobPtr
{
  friend bool operator==(const StrBlobPtr &, const StrBlobPtr &);
  friend bool operator!=(const StrBlobPtr &, const StrBlobPtr &);
  friend bool operator<(const StrBlobPtr &, const StrBlobPtr &);
 public:
  string &operator[](size_t n);
  const string &operator[](size_t n) const;
 StrBlobPtr(): curr(0) {}
 StrBlobPtr(StrBlob &a, size_t sz = 0):
  wptr(a.data), curr(sz) {}
  string &deref() const;
  StrBlobPtr &incr();
 private:
  shared_ptr<vector<string>> check(size_t,const string&) const;
  weak_ptr<vector<string>> wptr;
  size_t curr;
};
class ConstStrBlobPtr
{
 public:
 ConstStrBlobPtr(): curr(0) {}
 ConstStrBlobPtr(const StrBlob &a, size_t sz = 0):
  wptr(a.data), curr(sz) {}
  const string &deref() const;
  ConstStrBlobPtr &incr();
 private:
  shared_ptr<vector<string>> check(size_t,const string&) const;
  weak_ptr<vector<string>> wptr;
  size_t curr;
};
#endif

Exercise 13.26

1
2
// See StrBlob.h

StrBlob.h

Exercise 13.27

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class HasPtr
{
public:
  HasPtr(const string &s = string()):
    ps(new string(s)), use(new size_t(1)), i(0) {}
  HasPtr(const HasPtr &h): ps(new string(*h.ps)), use(h.use),i(h.i) {++*use;}
  HasPtr &operator=(const HasPtr &);
  ~HasPtr();
private:
  string *ps;
  size_t *use;
  int i;
};
HasPtr::~HasPtr()
{
  if(--*use == 0)
    {
      delete ps;
      delete use;
    }
}
HasPtr& HasPtr::operator=(const HasPtr &h)
{
  ++*h.use;
  if(--*use == 0)
    {
      delete ps;
      delete use;
    }
  ps = h.ps;
  i = h.i;
  use = h.use;
  return *this;
}

Exercise 13.28

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// recursive widely used here.
class BinStrTree;
class TreeNode
{
private:
  string value;
  int count;
  TreeNode *left;
  TreeNode *right;
public:
  TreeNode(): count(0), left(nullptr), right(nullptr) {}
  TreeNode(const TreeNode &t);
  TreeNode &operator=(const TreeNode &t);
  ~TreeNode();
};
TreeNode::TreeNode(const TreeNode &t): value(t.value), count(t.count)
{
  if(t.left)
    left = new TreeNode(*t.left);
  if(t.right)
    right = new TreeNode(*t.right);
}
TreeNode &TreeNode::operator=(const TreeNode &t)
{
  auto new_left = new TreeNode(*t.left);
  auto new_right = new TreeNode(*t.right);
  delete left;
  delete right;
  left = new_left;
  right = new_right;
  value = t.value;
  count = t.count;
  return *this;
}
TreeNode::~TreeNode()
{
  delete left;
  delete right;
}
class BinStrTree
{
private:
  TreeNode *root;
public:
  BinStrTree() = default;
  BinStrTree(const BinStrTree &b): root(new TreeNode(*b.root)) {}
  BinStrTree(const TreeNode &t): root(new TreeNode(t)) {}
  BinStrTree &operator=(const BinStrTree &b);
  ~BinStrTree();
};
BinStrTree &BinStrTree::operator=(const BinStrTree &b)
{
  auto new_root = new TreeNode(*b.root);
  delete root;
  root = new_root;
  return *this;
}
BinStrTree::~BinStrTree()
{
  delete root;
}

Exercise 13.29

1
2
// Because the swap version called in 'HasPtr &HasPtr::operator=(HasPtr rhs)' is the type specified version, not std::swap.

Exercise 13.30

1
2
// See HasPtr.h.

HasPtr.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class HasPtr
{
  friend void swap(HasPtr &, HasPtr &);
 public:
 HasPtr(const string &s = string()): ps(new string(s)), i(0) {}
  HasPtr(HasPtr &&h) noexcept: ps(h.ps), i(h.i) {h.ps = nullptr;}
 HasPtr(const HasPtr &h): ps(new string(*h.ps)), i(h.i) {}
  HasPtr &operator=(const HasPtr &);
  HasPtr &operator=(HasPtr &&) noexcept;
  ~HasPtr() {delete ps;};
 private:
  string *ps;
  int i;
};
HasPtr& HasPtr::operator=(HasPtr &&h) noexcept
{
  if(this != &h)
    {
      delete ps;
      ps = h.ps;
      i = h.i;
      h.ps = nullptr;
    }
  return *this;
}
void swap(string *p1, string *p2)
{
  string *temp = p1;
  p1 = p2;
  p2 = temp;
}
void swap(HasPtr &h1, HasPtr &h2)
{
  cout << "swap(HasPtr &h1, HasPtr &h2)" << endl;
  swap(h1.ps, h2.ps);
  swap(h1.i, h2.i);
}
HasPtr& HasPtr::operator=(const HasPtr& h)
{
  // swap(*this, h);
  ps = new string(*h.ps);
  i = h.i;
  return *this;
}

Exercise 13.31

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class HasPtr
{
  friend void swap(HasPtr &, HasPtr &);
public:
  HasPtr(const string &s = string()):
    ps(new string(s)), i(0) {}
  HasPtr(const HasPtr &h): ps(new string(*h.ps)), i(h.i) {}
  HasPtr &operator=(HasPtr);
  bool operator<(HasPtr &);
  ~HasPtr() {delete ps;};
private:
  string *ps;
  int i;
};
bool HasPtr::operator<(HasPtr &h)
{
  return *ps < *(h.ps);
}
void swap(string *p1, string *p2)
{
  string *temp = p1;
  p1 = p2;
  p2 = temp;
}
void swap(HasPtr &h1, HasPtr &h2)
{
  cout << "swap(HasPtr &h1, HasPtr &h2)" << endl;
  swap(h1.ps, h2.ps);
  swap(h1.i, h2.i);
}
HasPtr& HasPtr::operator=(HasPtr h)
{
  swap(*this, h);
  return *this;
}
int main()
{
  vector<HasPtr> vh;
  vh.push_back(HasPtr("hello"));
  vh.push_back(HasPtr("you"));
  vh.push_back(HasPtr("me"));
  sort(vh.begin(), vh.end());
}

Exercise 13.32

1
2
3
// Gets fewer benefits than the value-like class;
// only saves the work of copy initialization of the HasPtr temp object, and the increase & decrease of reference counts.

Exercise 13.33

1
2
3
4
// Both functions aim to modify the exact Folder object passed in.
// if the type of parameter of save and remove is 'Folder': that would use the argument to copy initialize the paremeter, both functions aim to modify the Folder object passed in, not the copy of it.
// if it is 'const Folder &', it is impossibile to modify a const Folder object, the expressions within function body are illegal.

Exercise 13.34

1
2
// See Message.h.

Message.h

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
class Message;
class Folder
{
  set<Message*> messages;
 public:
  Folder() = default;
  Folder(const Folder &);
  ~Folder();
  void addMsg(Message *m) {messages.insert(m);}
  void remMsg(Message *m) {messages.erase(m);}
};
class Message
{
  friend class Folder;
  friend void swap(Message &, Message &);
 public:
  explicit Message(const string &str = ""): contents(str) {}
  Message(const Message &);
  Message(Message &&) noexcept;
  Message& operator=(const Message &);
  Message& operator=(Message &&) noexcept;
  ~Message();
  void save(Folder &);
  void remove(Folder &);
  void addFod(Folder *f) {folders.insert(f);}
  void remFod(Folder *f) {folders.erase(f);}
 private:
  string contents;
  set<Folder*> folders;
  void add_to_Folders(const Message &);
  void remove_from_Folders();
  void move_Folders(Message *);
};
void Message::save(Folder &f)
{
  folders.insert(&f);
  f.addMsg(this);
}
void Message::remove(Folder &f)
{
  folders.erase(&f);
  f.remMsg(this);
}
void Message::add_to_Folders(const Message &m)
{
  for(auto f : m.folders)
    f->addMsg(this);
}
Message::Message(const Message &m):
contents(m.contents), folders(m.folders)
{
  add_to_Folders(m);
}
Message::Message(Message &&m) noexcept: contents(std::move(m.contents))
{
  move_Folders(&m);
}
void Message::remove_from_Folders()
{
  for(auto f : folders)
    f->remMsg(this);
}
Message::~Message()
{
  remove_from_Folders();
}
Message& Message::operator=(const Message &rhs)
{
  remove_from_Folders();
  contents = rhs.contents;
  folders = rhs.folders;
  add_to_Folders(rhs);
  return *this;
}
  Message& Message::operator=(Message &&m) noexcept
  {
    if(this != &m)
      {
      remove_from_Folders();
      contents = std::move(m.contents);
      move_Folders(&m);
      }
    return *this;
  }
    void swap(Message &lhs, Message &rhs)
    {
      for(auto f : lhs.folders)
      f->remMsg(&lhs);
      for(auto f : rhs.folders)
      f->remMsg(&rhs);
      swap(lhs.folders, rhs.folders);
      swap(lhs.contents, rhs.contents);
      for(auto f : lhs.folders)
      f->addMsg(&lhs);
      for(auto f : rhs.folders)
      f->addMsg(&rhs);
    }
Folder::~Folder()
{
  for(auto &m : messages)
    m->remove(*this);
}
Folder::Folder(const Folder &f)
{
  messages.clear();
  for(auto &m : messages)
    m->remove(*this);
  for(auto &m : f.messages)
    m->save(*this);
}
void Message::move_Folders(Message *m)
{
  folders = std::move(m->folders);
  for(auto f : folders)
    {
      f->remMsg(m);
      f->addMsg(this);
    }
  m->folders.clear();
}

Exercise 13.35

1
2
3
4
5
// is using synthesized version of copy control members:
// for copy constructor: no folder contains the pointer which points to this newly constructed Message object;
// for destructor: even destructed, folders still contains the pointer which points to the invalid memory space which once stored this destructed object.
// for copy assignment constructor: this case is a combination of copy constructor and destructor illustrated above, the folders-message relationship stays the same but it should have been updated.

Exercise 13.36

1
2
// See Message.h.

Message.h

Exercise 13.37

1
2
// see Message.h.

Message.h

Exercise 13.38

1
2
3
4
5
6
7
// void swap(message &lhs, Message &rhs)
// its implementation really swaps the contents of the two passed in arguments,
// and Message &Message::operator=(const Message &rhs) has a const reference type parameter, if this copy assignment operator calls swap(message &lhs,, Messag &rhs),
// the operations within swap is illegal;
// even though there is a way of copy initializing a non-const temp object from rhs, and pass it to swap as second argument, which would make it reasonable;
// but there is a lot of unnecessary work defined in swap for copy assignment operator; for example, destruction and rebuild of the message-folders relationship of the second parameter is a waste of time and space.

Exercise 13.39

1
2
// see StrVec.h.

StrVec.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#ifndef STRVEC_H
#define STRVEC_H
class 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(const StrVec &);
  StrVec(StrVec &&) noexcept;
  StrVec(const initializer_list<string> &is);
  StrVec &operator=(const StrVec &);
  StrVec &operator=(StrVec &&) noexcept;
  StrVec &operator=(const initializer_list<string> &);
  string &operator[](size_t n);
  const string &operator[](size_t n) const;
  ~StrVec();
  void push_back(const string &);
  size_t size() const {return first_free - elements;}
  size_t capacity() const {return cap - elements;}
  void reserve(size_t);
  void resize(size_t n, string s = "");
  string *begin() const {return elements;}
  string *end() const {return first_free;}
 private:
  allocator<string> alloc; /* it using static, 'undefined reference' reported when compile */
  void chk_n_alloc()
  {if(size() == capacity()) reallocate();}
  pair<string *, string *> alloc_n_copy
    (const string *, const string *);
  void free();
  void reallocate(size_t n = 0);
  string *elements;
  string *first_free;
  string *cap;
};
#endif

Exercise 13.40

1
2
// See StrVec.h

StrVec.h

Exercise 13.41

1
2
3
// 'first_free++' keeps the original value of i as temp, increase first_free itself, and return the temp;
// if use '++first_free', increase first_free, and return it; that is not right, if use that version, it would not construct the specified string object at right place but the place next to it, which means, this operation would skip the right place.

Exercise 13.42

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// See TextQuery.h.
void runQueries(ifstream &infile)
{
  TextQuery tq(infile);
  while(true)
    {
      cout << "Enter word to look for, or q to quit: ";
      string s;
      if(!(cin >> s) || s == "q") break;
      print(cout, tq.query(s)) << endl;
    }
}
int main(int argc, char *argv[])
{
  ifstream ifs(argv[1]);
  runQueries(ifs);
}

TextQuery.h

Exercise 13.43

1
2
3
4
// see StrVec.h.
// the original one is better, use for loop, it could destroy elememts in a reverse order as required, that form is more flexible.
// thr for_each one could not destroy elements in a reverse order, for the string poniter could not behave like a reverse iterator, the form of this for_each syntax has its advantages of simple and easy to use, but has more restrictions than for type syntax.

StrVec.h

Exercise 13.44

1
2
3
4
5
6
7
8
9
// See String.h.
int main()
{
  String s1;
  String s2("Sing for me!");
  s1.print();
  s2.print();
}

String.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#ifndef STRING_H_EX
#define STRING_H_EX
class String
{
  friend bool operator==(const String &, const String &);
  friend bool operator!=(const String &, const String &);
  friend bool operator<(const String &, const String &);
 public:
 String(): elements(nullptr), first_free(nullptr), cap(nullptr) {}
  String(const char *);
  String(const String &);
  String(String &&) noexcept;
  String &operator=(const String &);
  String &operator=(String &&) noexcept;
  char &operator[](size_t n);
  const char &operator[](size_t n) const;
  ~String();
  void push_back(const char &);
  size_t size() const {return first_free - elements;}
  size_t capacity() const {return cap - elements;}
  char *begin() const {return elements;}
  char *end() const {return first_free;}
  ostream &print();
 private:
  allocator<char> alloc; /* it using static, 'undefined reference' reported when compile */
  void chk_n_alloc()
  {if(size() == capacity()) reallocate();}
  pair<char *, char *> alloc_n_copy
    (const char *, const char *);
  void free();
  void reallocate(size_t n = 0);
  char *elements;
  char *first_free;
  char *cap;
};
#endif

Exercise 13.45

1
2
3
4
// Difference between lvalue reference and rvalue reference:
// lvalue reference: could not bind to expressions which require convertion, literal constants or erpressions which return rvalue; could bind to expressions which returns lvalue.
// rvalue reference; just the opposite to the lvalue reference.

Exercise 13.46

1
2
3
4
5
6
7
// int f();
// vector<int> vi(100);
// int &&r1 = f();
// int &r2 = vi[0];
// int &r3 = r1;
// int &&r4 = vi[0] * f();

Exercise 13.47

1
2
// see String.h.

String.h

Exercise 13.48

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
int main()
{
  std::vector<String> vs;
  std::cout << "Initialize vector<String> object" << std::endl;
  String s("hello");
  std::cout << "Initialize String object" << std::endl;
  std::cout << "Push first" << std::endl;
  vs.push_back(s);
  std::cout << "Push second" << std::endl;
  vs.push_back(s);
  std::cout << "Push third" << std::endl;
  // vs.push_back(String("word"));
  vs.push_back(s);
}
// echo "String::String(const String &s) six times
// for there is a memory reallocation processing during push_back.

Exercise 13.49

1
2
// See StrVec.h, String.h, Message.h.

Message.h

StrVec.h

String.h

Exercise 13.50

1
2
// When the vector<String> object does not have enough space and then processes memory reallocation, it would use move constructor instead of copy constructor of String type during the reallocation of original elements.

Exercise 13.51

1
2
3
// unique_ptr supports move constructor and move-assignment operator;
// the source objects are to be destroyed.

Exercise 13.52

1
2
3
4
5
6
7
8
// HasPtr hp = hp2;
// hp2 is lvaule, using copy constructor to initialize hp, hp2 stays unchanged, hp is the copy of hp2;
// HasPtr hp = std::move(hp2);
// std::move(hp2) returns a rvalue reference binded to lvalue hp2;
// so using move constructor to initialize hp, hp gets the control of the resouces owned by hp2;
// while hp2 is valid, in a destruction-safe state, but there is no gurantee of the hp2's contents.
// as illustreted in text, both expressions use constructors, not assignment operators, so parameter rhs is not involved in this context.

Exercise 13.53

1
2
3
4
5
// See HasPtr.h.
// The efficiency of copy-assgnment operator of HasPtr class type is not good enough, for that there is a copy constructor called when argument passed in, and the exchanging of value within function body may be unnecessary.
// The new move-assignment operator takes control of the resources owned by another source object, and makes sure that object in a valid and destruction-safe state.
// The copy-swap operator implements assignment by value exchanging, finally the data member ps points to a new allocated string, which is a copy of string object pointed by right operand's data member ps, the right operand stays unchanged.

HasPtr.h

Exercise 13.54

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int main()
{
  HasPtr h("helloworld");
  HasPtr h2 = h;
  HasPtr h3 = std::move(h2);
  h2 = h;
  h2 = std::move(h3);
  // if the original copy-assignment operator stay unchanged
  // compiler warns:
  // ambiguous overload for 'operator=' (operand types are 'HasPtr'
  // and 'std::remove_reference<HasPtr&>::type {aka HasPtr}')
}

Exercise 13.55

1
2
// See StrBlob.h.

StrBlob.h

Exercise 13.56

1
2
3
4
5
6
7
8
9
///////////////////////////////
// Foo Foo::sorted() const & //
// {			     //
//   Foo ret(*this);	     //
//   return ret.sorted();    //
// }			     //
///////////////////////////////
// The return statement causes infinite recursively calling, exceeds the max stack depth.

Exercise 13.57

1
2
// Passed, got the desired result.

Exercise 13.58

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Foo
{
public:
  Foo sorted() &&;
  Foo sorted() const &;
private:
  vector<int> data;
};
// Foo Foo::sorted() const &
// {
//   cout << "Foo Foo::sorted() const &" << endl;
//   Foo ret(*this);
//   return ret.sorted();		// segment fault
// }
Foo Foo::sorted() const &
{
  return Foo(*this).sorted();	// passed
}
Foo Foo::sorted() &&
{
  sort(data.begin(), data.end());
  return *this;
}
int main()
{
  Foo f;
  f.sorted();
}