Exercise 15.1

1
2
3
// What is a virtual member?
// For some member functions, base class demands its derived class defines its own version, then base class defines them as virtual functions.

Exercise 15.2

1
2
3
// members marked as protected could be visted by derived class, while still could not be visited by other user code;
// members marked as private could not be visited by derived class and other user code.

Exercise 15.3

1
2
// See Quote.h.

Quote.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
#ifndef QUOTE_H
#define QUOTE_H
class Quote
{
public:
  Quote() {cout << "Quote()" << endl;}
  Quote(const string &book, double sales_price):
  bookNo(book), price(sales_price)
  {cout << "Quote(cosnt string &, double)" << endl;}
  string isbn() const {return bookNo;}
  Quote(const Quote &q)
    {bookNo = q.bookNo; price = q.price; cout << "Quote(const Quote &)" << endl;}
  Quote &operator=(const Quote &q)
    {
      bookNo = q.bookNo;
      price = q.price;
      cout << "Quote &operator=(const Quote &)" << endl;
      return *this;
    }
  Quote(Quote &&q)
    {bookNo = std::move(q.bookNo); price = q.price; cout << "Quote(Quote &&)" << endl;}
  Quote &operator=(Quote &&q)
    {
      bookNo = std::move(q.bookNo);
      price = q.price;
      cout << "Quote &operator=(Quote &&)" << endl;
      return *this;
    }
  virtual double net_price(size_t n) const
  {return n * price;}
  virtual ostream &debug() const
  {
    cout << "bookNo " << bookNo << " price " << price;
    return cout;
  }
  virtual Quote* clone() const & {return new Quote(*this);}
  virtual Quote* clone() && {return new Quote(std::move(*this));}
  virtual ~Quote() {cout << "~Quote()" << endl;};
private:
  string bookNo;
protected:
  double price = 0.0;
};
double print_total(ostream &os, const Quote &item, size_t n)
{
  double ret = item.net_price(n);
  os << "ISBN: " << item.isbn()
     << " # sold: " << n << " total due: " << ret << endl;
  return ret;
}
#endif

Exercise 15.4

1
2
3
4
5
6
7
8
// class Base { ... };
// a. class Derived : public Derived { ... }
// illegal, one class could not derive from itself.
// b. class Derived : private Base { ... }
// illegal, this one as definition considered legal.
// c. class Derived : public Base;
// illegal, declaration of a class should not include its class derivation list.

Exercise 15.5

1
2
// See Bulk_quote.h.

Bulk_quote.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
#ifndef BULK_QUOTE_H
#define BULK_QUOTE_H
class Bulk_quote: public Quote
{
 public:
  using Quote::Quote;
 Bulk_quote(): Quote() {cout << "Bulk_quote()" << endl;};
 Bulk_quote(const Bulk_quote &b): Quote(b), min_qty(b.min_qty), discount(b.discount)
    {cout << "Bulk_Quote(const Bulk_quote &)" << endl;}
 Bulk_quote(Bulk_quote &&b): Quote(std::move(b)), min_qty(b.min_qty), discount(b.discount)
    {cout << "Bulk_quote(Bulk_quote &&b)" << endl;}
  Bulk_quote &operator=(const Bulk_quote &b)
    {
      Quote::operator=(b);
      min_qty = b.min_qty;
      discount = b.discount;
      {cout << "operator=(const Bulk_quote &)" << endl;}
      return *this;
    }
  Bulk_quote &operator=(Bulk_quote &&b)
    {
      Quote::operator=(std::move(b));
      min_qty = b.min_qty;
      discount = b.discount;
      {cout << "operator=(Bulk_quote &&)" << endl;}
      return *this;
    }
 Bulk_quote(const string & book, double p, size_t qty, double disc):
  Quote(book, p), min_qty(qty), discount(disc) {}
  double net_price(size_t) const override;
  ostream &debug() const override;
  Bulk_quote *clone() const & override {return new Bulk_quote(*this);}
  Bulk_quote *clone() && override {return new Bulk_quote(std::move(*this));}
  ~Bulk_quote() override {cout << "~Bulk_quote()" << endl;}
 private:
  size_t min_qty = 0;
  double discount = 0.0;
};
double Bulk_quote::net_price(size_t cnt) const
{
  if(cnt >= min_qty)
    return cnt * (1 - discount) * price;
  else
    return cnt * price;
}
ostream &Bulk_quote::debug() const
{
  Quote::debug();
  cout << " min_qty " << min_qty << " discount " << discount;
  return cout;
}
#endif

Exercise 15.6

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int main()
{
  Quote a("123", 21.1);
  Bulk_quote b("123", 21.1, 5, 0.05);
  Limit_Bulk_quote c("123", 21.1, 5, 0.05);
  print_total(std::cout, a, 5);
  a.debug() << endl;
  print_total(std::cout, b, 5);
  b.debug() << endl;
  print_total(std::cout, c, 6);
  c.debug() << endl;
}

Exercise 15.7

1
2
// See Limit_Bulk_quote.h.

Limit_Bulk_quote.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
#ifndef LIMIT_BULK_QUOTE_H
#define LIMIT_BULK_QUOTE_H
class Limit_Bulk_quote: public Quote
{
 public:
  Limit_Bulk_quote() = default;
 Limit_Bulk_quote(const string & book, double p, size_t qty, double disc):
  Quote(book, p), max_qty(qty), discount(disc) {}
  double net_price(size_t) const override;
  ostream &debug() const override;
 private:
  size_t max_qty = 0;
  double discount = 0.0;
};
double Limit_Bulk_quote::net_price(size_t cnt) const
{
  if(cnt <= max_qty)
    return cnt * (1 - discount) * price;
  else
    return max_qty * (1 - discount) * price + (cnt - max_qty) * price;
}
ostream &Limit_Bulk_quote::debug() const
{
  Quote::debug();
  cout << " max_qty " << max_qty << " discount " << discount;
  return cout;
}
#endif

Exercise 15.8

1
2
3
4
5
6
7
// static type
// which is used in declaration of variable, or evaluated from expression;
// known in compile process.
// dynamic type
// which is the type of object allocated in memory and represented by variable or expression;
// known until run-time.

Exercise 15.9

1
2
3
4
5
6
7
8
9
// When bind a pointer of a reference of base class type to a object of derived class type, the static type is different from the dynamic type.
// class Base { ... };
// class Derived: public Base { ... };
// int f(const Base &b) { ... }
// Derived d;
// Base &b = d;
// Base *p = &d;
// f(d);

Exercise 15.10

1
2
3
4
5
// ifstream object if passed in as argument to read function of Sales_data object;
// read function's first parameter's type is 'istream &';
// while ifstream class type is derived from istream class type;
// then this parameter could be initialized by ifstream object, binded to the istream sub-object of ifstream object;

Exercise 15.11

1
2
// See Quote.h, Bulk_quote.h and Limit_quote.h.

Bulk_quote.h

Quote.h

Limit_quote.h

1
2

Exercise 15.12

1
2
3
4
5
// Unnecessary;
// 'final' implicitly means that this function is a override virtual function of the virtual function within base class.
// Am I right?
// Emacs & cquery & flycheck do not warn me when I replace override with final.

Exercise 15.13

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class base
{
public:
  string name() {return basename;}
  virtual void print(ostream &os) {os << basename;};
private:
  string basename;
};
class derived: public base
{
public:
  // void print(ostream &os) {print(os); os << " " << i;} // call itself within function body causes infinite recursion.
  void print(ostream &os) override {base::print(os); os << " " << i;}
private:
  int i;
};

Exercise 15.14

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
int main()
{
  base bobj;
  derived dobj;
  base *bp1 = &bobj;
  base *bp2 = &dobj;
  base &br1 = bobj;
  base &br2 = dobj;
  bobj.print(cout);		// base::print
  dobj.print(cout);		// derived::print
  bp1->name();			// base::name
  bp2->name();			// base::name
  br1.print(cout); 		// base::print
  br2.print(cout);		// derived::print
}

Exercise 15.15

1
2
// See Disc_quote.h and Bulk_quote-1515.h.

Bulk_quote-1515.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
#ifndef BULK_QUOTE_H
#define BULK_QUOTE_H
class Bulk_quote: public Disc_quote
{
 public:
  Bulk_quote() = default;
 Bulk_quote(const string & book, double p, size_t qty, double disc):
  Disc_quote(book, p, qty, disc) {}
  double net_price(size_t) const override;
  ostream &debug() const override;
};
double Bulk_quote::net_price(size_t cnt) const
{
  if(cnt >= quantity)
    return cnt * (1 - discount) * price;
  else
    return cnt * price;
}
ostream &Bulk_quote::debug() const
{
  Quote::debug();
  cout << " min_qty " << quantity << " discount " << discount;
  return cout;
}
#endif

Disc_quote.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Disc_quote: public Quote
{
 public:
  Disc_quote() = default;
  Disc_quote(const string &book, double price,
	   size_t qty, double disc):
  Quote(book, price), quantity(qty), discount(disc) {}
  double net_price(size_t) const = 0;
 protected:
  size_t quantity = 0;
  double discount = 0.0;
};

Exercise 15.16

1
2
// See Limit_Bulk_quote-1516.h.

Limit_Bulk_quote-1516.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
#ifndef LIMIT_BULK_QUOTE_H
#define LIMIT_BULK_QUOTE_H
class Limit_Bulk_quote: public Disc_quote
{
 public:
  Limit_Bulk_quote() = default;
 Limit_Bulk_quote(const string & book, double p, size_t qty, double disc):
  Disc_quote(book, p, qty, disc) {}
  double net_price(size_t) const override;
  ostream &debug() const override;
};
double Limit_Bulk_quote::net_price(size_t cnt) const
{
  if(cnt <= quantity)
    return cnt * (1 - discount) * price;
  else
    return quantity * (1 - discount) * price + (cnt - quantity) * price;
}
ostream &Limit_Bulk_quote::debug() const
{
  Quote::debug();
  cout << " max_qty " << quantity << " discount " << discount;
  return cout;
}
#endif

Exercise 15.17

1
2
3
4
5
6
int main()
{
  Disc_quote d;
  // variable type 'Disc_quote' is an abstract class
}

Exercise 15.18

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Base *p = &dl;
// d1: Pub_Derv
// legal
// p = &d2;
// d2: Priv_Derv
// illegal, for the derivation access specifier of Priv_Derv is private, conversion from Priv_Derv to its base class Base is forbidden;
// p = &d3;
// d3: Prot_Derv
// illegal, for the derivation access spedifier of Prot_Derv is protected, conversion from Priv_Derv to its base class Base is forbidden;
// p = &dd1;
// dd1: Derived_from_Public
// legal
// p = &dd2;
// dd2: Derived_from_Private
// illegal, same reason as the second one;
// p = &dd3;
// dd3: Derived_from_Protected
// illegal, same reason as the third one.

Exercise 15.19

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// void memfcn(Base &b) { b = &this; }
// as member function
// for Base:
// legal;
// for Pub_Derv:
// legal;
// for Pro_Derv;
// legal;
// for Priv_Derv:
// legal;
// for Derived_from_Public:
// legal;
// for Derived_from_Private:
// illegal;
// for the derivation access specifier of Priv_Derv is private, member or friend of derivaed class of Priv_Derv would nerver call the conversion from Derived_from_private to Base class.
// for Derived_from_Protected:
// legal;

Exercise 15.20

 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
class Base
{
public:
  void memfcn(Base &b) {b = *this;}
  void pub_mem();
protected:
  int prot_mem;
private:
  char priv_mem;
};
struct Pub_Derv: public Base
{
  void memfcn(Base &b) {b = *this;}
  int f() {return prot_mem;}
};
struct Priv_Derv: private Base
{
  void memfcn(Base &b) {b = *this;}
  int f1() const {return prot_mem;}
};
struct Prot_Derv: protected Base
{
  void memfcn(Base &b) {b = *this;}
  int f2() const {return prot_mem;}
};
struct Derived_from_Public: public Pub_Derv
{
  void memfcn(Base &b) {b = *this;}
  int use_base() {return prot_mem;};
};
struct Derived_from_Private: public Priv_Derv
{
  void memfcn(Base &b) {b = *this;} // illegal
  int i;
};
struct Derived_from_Protected: public Prot_Derv
{
  void memfcn(Base &b) {b = *this;}
  int use_base() {return prot_mem;}
};
int main()
{
  Pub_Derv d1;
  Priv_Derv d2;
  Prot_Derv d3;
  Derived_from_Public dd1;
  Derived_from_Private dd2;
  Derived_from_Protected dd3;

  Base *p = &d1;		// legal;
  p = &d2;			// illegal;
  p = &d3;			// illegal;
  p = &dd1;			// legal;
  p = &dd2;			// illegal;
  p = &dd3;			// illegal;
}

Exercise 15.21

 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
class Employee
{
private:
  string name;
  string department;
  string address;
  string contact;
protected:
  int salary;
public:
  Employee() = default;
  Employee(string n, string d, string a, string c, int s):
    name(n), department(d), address(a), contact(c), salary(s) {}
  string get_name() {return name;}
  string get_department() {return department;}
  string get_address() {return address;}
  string get_contact() {return contact;}
  virtual int get_payment() {return salary;}
};
class Manager: public Employee
{
protected:
  int bonus;
public:
  Manager() = default;
  Manager(string n, string d, string a, string c, int s, int b):
    Employee(n, d, a, c, s), bonus(b) {}
  int get_payment() override {return salary + bonus;}
};
class CEO: public Manager
{
protected:
  int share;
public:
  CEO() = default;
  CEO(string n, string d, string a, string c, int s, int b, int sh):
    Manager(n, d, a, c, s, b), share(sh) {}
  int get_payment() override {return salary + bonus + share;}
};

Exercise 15.22

1
2
// See 15.21.cpp.

15.21.cpp

 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
class Employee
{
private:
  string name;
  string department;
  string address;
  string contact;
protected:
  int salary;
public:
  Employee() = default;
  Employee(string n, string d, string a, string c, int s):
    name(n), department(d), address(a), contact(c), salary(s) {}
  string get_name() {return name;}
  string get_department() {return department;}
  string get_address() {return address;}
  string get_contact() {return contact;}
  virtual int get_payment() {return salary;}
};
class Manager: public Employee
{
protected:
  int bonus;
public:
  Manager() = default;
  Manager(string n, string d, string a, string c, int s, int b):
    Employee(n, d, a, c, s), bonus(b) {}
  int get_payment() override {return salary + bonus;}
};
class CEO: public Manager
{
protected:
  int share;
public:
  CEO() = default;
  CEO(string n, string d, string a, string c, int s, int b, int sh):
    Manager(n, d, a, c, s, b), share(sh) {}
  int get_payment() override {return salary + bonus + share;}
};

Exercise 15.23

 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
class Base
{
public:
  virtual int fcn();
};
class D1: public Base
{
public:
  int fcn() override;
  int fcn(int);
  virtual void f2();
};
class D2: public D1
{
public:
  int fcn(int);
  int fcn() override;
  void f2() override;
};
int main()
{
  Base bobj;
  D1 d1obj;
  D2 d2obj;
  Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;
  // ...
  bp2->fcn();			// D1::fcn()
  // ...
  // others stay unchanged
}

Exercise 15.24

1
2
3
// Base class and its derived class require a virtual destructor;
// a virtual destructor of class type should release the resource occupied by the object of that type.

Exercise 15.25

1
2
3
4
// Due to the fact that we have defined a constructor with paremeters for Disc_quote class, then there would be no default constructor fot this class;
// but there is a default constructor defined within Bulk_quote class, which is a derived class of Disc_quote; when this constructor is called, it would call the default constructor of Disc_quote class to construct a Disc_quote sub-object;
// so we must define a default constructor for Disc_quote class, or the calling of default constructor of Bulk_quote would fail.

Exercise 15.26

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// See Quote.h and Bulk_quote.h.
int main()
{
  Quote q;
  // Bulk_quote b;
  // Quote q2 = q;
  // Quote q3 = std::move(q);
  // q = q2;
  // q = std::move(q3);
  // Bulk_quote b2 = b;
  // Bulk_quote b3 = std::move(b);
  // b = b2;
  // b = std::move(b2);
}

Bulk_quote.h

Quote.h

Exercise 15.27

1
2
// See Bulk_quote.h.

Bulk_quote.h

Exercise 15.28

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
int main()
{
  vector<Quote> basket1;
  basket1.push_back(Bulk_quote("123", 50, 10, 0.10));
  basket1.push_back(Bulk_quote("234", 60, 15, 0.15));
  double total = 0.0;
  for(const auto &p : basket1)
    total += p.net_price(20);
  cout << total << endl;
  cout << string("=", 15) << endl;
  vector<shared_ptr<Quote>> basket2;
  basket2.push_back(make_shared<Bulk_quote>("123", 50, 10, 0.10));
  basket2.push_back(make_shared<Bulk_quote>("234", 60, 15, 0.15));
  total = 0.0;
  for(const auto &p : basket2)
    total += p->net_price(20);
  cout << total << endl;
}

Exercise 15.29

1
2
3
4
5
// See 15.28.cpp.
// The output is different from the previous one;
// for the first version of basket's element type is Quote, when pushed a Bulk_quote object into this vector, a Quote object as a copy of the Qutoe part of Bulk_quote object is pushed in, at last we call the Quote's net_price version;
// for the next version of basket's element type is shared_ptr<quote>, a shared_ptr<quote> could be binded to a Bulk_quote object because Bulk_quote public derived from Quote class, then the version of net_price is determined by object's dynamic type.

15.28.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
int main()
{
  vector<Quote> basket1;
  basket1.push_back(Bulk_quote("123", 50, 10, 0.10));
  basket1.push_back(Bulk_quote("234", 60, 15, 0.15));
  double total = 0.0;
  for(const auto &p : basket1)
    total += p.net_price(20);
  cout << total << endl;
  cout << string("=", 15) << endl;
  vector<shared_ptr<Quote>> basket2;
  basket2.push_back(make_shared<Bulk_quote>("123", 50, 10, 0.10));
  basket2.push_back(make_shared<Bulk_quote>("234", 60, 15, 0.15));
  total = 0.0;
  for(const auto &p : basket2)
    total += p->net_price(20);
  cout << total << endl;
}

Exercise 15.30

 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 Basket
{
public:
  void add_item(const shared_ptr<Quote> &sale)
  {items.insert(sale);}
  void add_item(const Quote &sale)
  {items.insert(shared_ptr<Quote>(sale.clone()));}
  void add_item(Quote &&sale)
  {items.insert(shared_ptr<Quote>(std::move(sale).clone()));}
  double total_receipt(ostream &) const;
private:
  static bool compare(const shared_ptr<Quote> &lhs,
		    const shared_ptr<Quote> &rhs)
  {return lhs->isbn() < rhs->isbn();}
  multiset<shared_ptr<Quote>, decltype(compare)*>
  items{compare};
};
double Basket::total_receipt(ostream &os) const
{
  double sum = 0.0;
  for(auto iter = items.cbegin();
      iter != items.cend();
      iter = items.upper_bound(*iter))
    {sum += print_total(os, **iter, items.count(*iter));}
  os << "Total Sale: " << sum << endl;
  return sum;
}
int main()
{
  Basket basket;
  basket.add_item(make_shared<Quote>("123", 50));
  basket.add_item(make_shared<Quote>("234", 60));
  basket.total_receipt(cout);
}

Exercise 15.31

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// Query(s1) | Query(s2) & ~Query(s3);
// returns a Query object binded to a AndQuery object;
// Query(s1) | Query(s2) returns a Query object binded to a OrQuery object;
// ~Query(s3) returns a Query object binded to a NotQuery object;
// the base level are all WordQuery objects.
// Query(s1) | (Query(s2) & ~Query(s3));
// returns a Query object binded to a OrQuery object
// Query(s1) returns a Query object binded to WordQuery object;
// (Query(s2) & ~Query(s3)) returns a Query object binded to a AndQuery object;
// ~Query(s3) returns a Query object binded to a NotQuery object;
// the base level are all WordQuery objects.
// (Query(s1) & (Query(s2)) | (Query(s3) & Query(s4)))
// returns a Query object binded to OrQuery object
// (Query(s1) & (Query(s2))) returns a Query object binded to a AndQuery object;
// (Query(s3) & Query(s4)) returns a Query object binded to a AndQuery object;
// the base level are all WordQuery objects.

Exercise 15.32

1
2
3
4
5
6
7
8
9
// When a Query class type object is copied:
// would call the synthesized copy constructor of Query class, then the copy constructor of shared_ptr<Query_base> is called to constructor the new Query object's data member q, and the use count of both shared_ptr objects increase by 1;
// move
// since shared_ptr<Query_base> has a move constructor and there is no self-defined copy control members within Query, there is a synthesized move constructor, it is called to move construct the new Query object's data member q, the use-count stay unchanged;
// assignment
// synthesized copy-assignment operator or synthesized move-assignment operator: depenes on whether it is copy assignment or move assignment, this would call copy-assignment operator or move-assignment operator of shared_ptr<Query_base> to assign the left Query object's data member q, and use count increase by 1 or stay unchanged;
// destruction
// synthesized destructor: just call the destructor of shared_ptr<Query_base> member; depends on the use-count of this shared_ptr object, if it comes to 0 the destructor of the pointed object would be called(which destructor relies on its dynamic type).

Exercise 15.33

1
2
3
4
// This exercise should specify that the object is of derived class type whose base class is Query_base
// call the derived class type object's own copy control members;
// thus would call the synthesized version of copy control members of Query_base.

Exercise 15.34

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Query q = Query("fiery") & Query("bird") | Query("wind")
// a
// constructor:
// Query(const string &);
// WordQuery(const string &);
// AndQuery(const Query &, cosnt Query &);
// OrQuery(const Query &, const Query &);
// BinaryQuery(const Query &, const Query &, string);
// Query_base();
// Query(shared_ptr<Query_base>)
// b
// Query::rep() // missed in first attempt
// BinaryQuery::rep()
// WordQuery::rep()
// c
// OrQuery::eval
// AndQuery::eval
// WordQuery::eval

Exercise 15.36

1
2
3
4
5
6
int main()
{
  Query q = Query("fiery") & Query("bird") | Query("wind");
  cout << q;
}

Exercise 15.37

1
2
3
4
5
// Change the parameter type of 'const Query &' to 'shared_ptr<Query_base>' for NotQuery, BinaryQuery, AndQuery and OrQuery class's constructor, use passed in argument to initialize data member shared_ptr<Query_base>;
// the call of eval and rep inside class type now uses shared_ptr<Query_base> data members and '->' operator.
// three overload operator functions should change its paramter type from 'const Query &' to 'shared_ptr<Query_base>', and its return type from 'Query' to 'shared_ptr<Query_base>' too.
// in a word, the interface layer of Query class is replaced by direct operation of shared_ptr<Query_base>.

Exercise 15.38

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// BinaryQuery a = Query("fiery") & Query("bird")
// false;
// BinaryQuery is a abstract class, construct of this class type is illegal.
// AndQuery b = Query("fiery") & Query("bird")
// false
// operator&(const Query &, const Query &) returns a object of Query class type, not AndQuery, and there is no available way of conversion from Query to AndQuery.
// OrQuery b = Query("fiery") & Query("bird")
// false
// operator&(const Query &, const Query &) returns a object of Query class type, not OrQuery, and there is no available way of conversion from Query to OrQuery.

Exercise 15.39

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// See 15.35.h.
int main(int argc, char *argv[])
{
  ifstream ifs(argv[1]);
  TextQuery tq(ifs);
  Query q = Query("fiery") & Query("bird") | Query("wind");
  cout << q << endl;
  auto result = q.eval(tq);
  print(cout, result);
}

15.35.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 Query_base
{
  friend class Query;
protected:
  using line_no = TextQuery::line_no;
  Query_base() {cout << "Query_base::Query_base()" << endl;}
  virtual ~Query_base() = default;
private:
  virtual QueryResult eval(const TextQuery &) const = 0;
  virtual string rep() const = 0;
};
class Query
{
  friend Query operator~(const Query &);
  friend Query operator|(const Query &, const Query &);
  friend Query operator&(const Query &, const Query &);
public:
  Query(const string &);
  QueryResult eval(const TextQuery &t) const {return q->eval(t);}
  string rep() const {cout << "Query::rep" << endl; return q->rep();}
private:
 Query(shared_ptr<Query_base> query): q(query) {cout << "Query::Query(shared_ptr)" << endl;}
  shared_ptr<Query_base> q;
};
class WordQuery: public Query_base
{
  friend class Query;
 WordQuery(const string &s): query_word(s) {cout << "WordQuery::WordQuery(const string &)" << endl;}
  QueryResult eval(const TextQuery &t) const
  {return t.query(query_word);}
  string rep() const {cout << "WordQuery::rep" << endl; return query_word;}
  string query_word;
};
inline Query::Query(const string &s): q(new WordQuery(s)) {cout << "Query::Query(const string &)" << endl;}
class BinaryQuery: public Query_base
{
 protected:
 BinaryQuery(const Query &l, const Query &r, string s):
  lhs(l), rhs(r), opSym(s) {cout << "BinaryQuery::BinaryQuery" << endl;}
  string rep() const
  {
    cout << "BinaryQuery::rep" << endl;
    return "(" + lhs.rep() + " "
      + opSym + " "
      + rhs.rep() + ")";
  }
  Query lhs, rhs;
  string opSym;
};
class AndQuery: public BinaryQuery
{
  friend Query operator&(const Query &, const Query &);
 AndQuery(const Query &left, const Query &right):
  BinaryQuery(left, right, "&") {cout << "AndQuery::AndQuery" << endl;}
  QueryResult eval(const TextQuery &) const;
};
inline Query operator&(const Query &lhs, const Query &rhs)
{
  return shared_ptr<Query_base>(new AndQuery(lhs, rhs));
}
QueryResult
AndQuery::eval(const TextQuery &text) const
{
  auto left = lhs.eval(text), right = rhs.eval(text);
  auto ret_lines = make_shared<set<line_no>>();
  set_intersection(left.begin(), left.end(),
		right.begin(), right.end(),
		inserter(*ret_lines, ret_lines->begin()));
  return QueryResult(rep(), ret_lines, left.get_file());
}
class OrQuery: public BinaryQuery
{
  friend Query operator|(const Query &, const Query &);
 OrQuery(const Query &left, const Query &right):
  BinaryQuery(left, right, "|") {cout << "OrQUery::OrQuery" << endl;}
  QueryResult eval(const TextQuery &) const;
};
inline Query operator|(const Query &lhs, const Query &rhs)
{
  return shared_ptr<Query_base>(new OrQuery(lhs, rhs));
}
ostream &operator<<(ostream &os, const Query &query)
{
  return os << query.rep();
}
QueryResult OrQuery::eval(const TextQuery &text) const
{
  auto right = rhs.eval(text), left = lhs.eval(text);
  auto ret_lines = make_shared<set<line_no>>(left.begin(), left.end());
  ret_lines->insert(right.begin(), right.end());
  return QueryResult(rep(), ret_lines, left.get_file());
}
class NotQuery: public Query_base
{
  friend Query operator~(const Query &);
 NotQuery(const Query &q): query(q) {}
  string rep() const {return "~(" + query.rep() + ")";}
  QueryResult eval(const TextQuery &) const;
  Query query;
};
inline Query operator~(const Query &operand)
{
  return shared_ptr<Query_base>(new NotQuery(operand));
}
QueryResult
NotQuery::eval(const TextQuery &text) const
{
  auto result = query.eval(text);
  auto ret_lines = make_shared<set<line_no>>();
  auto beg = result.begin(), end = result.end();
  auto sz = result.get_file()->size();
  for(size_t n = 0; n != sz; ++n)
    {
      if(beg == end || *beg != n)
      ret_lines->insert(n);
      else if(beg != end)
      ++beg;
    }
  return QueryResult(rep(), ret_lines, result.get_file());
}

Exercise 15.40

1
2
3
4
// Within OrQuery, if rhs.eval(text) returns a QueryResult object which points to an empty set, the return value of OrQuery::eval would be same as the result of lhs.eval(text);
// vise versa;
// if both results of rhs.eval and lhs.eval point to an empty set, the return QueryResult of OrQuery::eval points to an empty set, too.

Exercise 15.41

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int main(int argc, char *argv[])
{
  ifstream ifs(argv[1]);
  TextQuery tq(ifs);
  Query q = Query("fiery") & Query("bird") | Query("wind");
  cout << q << endl;
  auto result = q.eval(tq);
  print(cout, result);
}

15.41.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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
class Query_base
{
  friend class Query;
protected:
  using line_no = TextQuery::line_no;
  Query_base() {}
  virtual ~Query_base() = default;
private:
  virtual QueryResult eval(const TextQuery &) const = 0;
  virtual string rep() const = 0;
};

class Query
{
  friend Query operator~(const Query &);
  friend Query operator|(const Query &, const Query &);
  friend Query operator&(const Query &, const Query &);
public:
  Query(const string &);
  Query(const Query &p): q(p.q), use(p.use) {++*use;}
  Query &operator=(const Query &p);
  QueryResult eval(const TextQuery &t) const {return q->eval(t);}
  string rep() const {return q->rep();}
  ~Query()
  {
    if(--*use == 0)
      {
	delete q;
	delete use;
      }
  }
private:
 Query(Query_base *query): q(query), use(new unsigned(1)) {}
  Query_base *q;
  unsigned *use;
};

Query &Query::operator=(const Query &p)
{
  ++*p.use;
  if(--*use == 0)
    {
      delete use;
      delete q;
    }
  use = p.use;
  q = p.q;
  return *this;
}

class WordQuery: public Query_base
{
  friend class Query;
  WordQuery(const string &s): query_word(s) {}
  QueryResult eval(const TextQuery &t) const
  {return t.query(query_word);}
  string rep() const {return query_word;}
  string query_word;
};

inline Query::Query(const string &s): q(new WordQuery(s)), use(new unsigned(1)) {}

class BinaryQuery: public Query_base
{
protected:
  BinaryQuery(const Query &l, const Query &r, string s):
    lhs(l), rhs(r), opSym(s) {}
  string rep() const
  {
    return "(" + lhs.rep() + " "
      + opSym + " "
      + rhs.rep() + ")";
  }
  Query lhs, rhs;
  string opSym;
};

class AndQuery: public BinaryQuery
{
  friend Query operator&(const Query &, const Query &);
  AndQuery(const Query &left, const Query &right):
    BinaryQuery(left, right, "&") {}
  QueryResult eval(const TextQuery &) const;
};

inline Query operator&(const Query &lhs, const Query &rhs)
{
  return new AndQuery(lhs, rhs);
}

QueryResult
AndQuery::eval(const TextQuery &text) const
{
  auto left = lhs.eval(text), right = rhs.eval(text);
  auto ret_lines = make_shared<set<line_no>>();
  set_intersection(left.begin(), left.end(),
		   right.begin(), right.end(),
		   inserter(*ret_lines, ret_lines->begin()));
  return QueryResult(rep(), ret_lines, left.get_file());
}

class OrQuery: public BinaryQuery
{
  friend Query operator|(const Query &, const Query &);
  OrQuery(const Query &left, const Query &right):
    BinaryQuery(left, right, "|") {}
  QueryResult eval(const TextQuery &) const;
};

inline Query operator|(const Query &lhs, const Query &rhs)
{
  return new OrQuery(lhs, rhs);
}

ostream &operator<<(ostream &os, const Query &query)
{
  return os << query.rep();
}

QueryResult OrQuery::eval(const TextQuery &text) const
{
  auto right = rhs.eval(text), left = lhs.eval(text);
  auto ret_lines = make_shared<set<line_no>>(left.begin(), left.end());
  ret_lines->insert(right.begin(), right.end());
  return QueryResult(rep(), ret_lines, left.get_file());
}

class NotQuery: public Query_base
{
  friend Query operator~(const Query &);
  NotQuery(const Query &q): query(q) {}
  string rep() const {return "~(" + query.rep() + ")";}
  QueryResult eval(const TextQuery &) const;
  Query query;
};

inline Query operator~(const Query &operand)
{
  return new NotQuery(operand);
}

QueryResult
NotQuery::eval(const TextQuery &text) const
{
  auto result = query.eval(text);
  auto ret_lines = make_shared<set<line_no>>();
  auto beg = result.begin(), end = result.end();
  auto sz = result.get_file()->size();
  for(size_t n = 0; n != sz; ++n)
    {
      if(beg == end || *beg != n)
	ret_lines->insert(n);
      else if(beg != end)
	++beg;
    }
  return QueryResult(rep(), ret_lines, result.get_file());
}

Exercise 15.42

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// implement (a)
// modify TextQuery.h
// see TextQuery-1542.h
int main(int argc, char *argv[])
{
  ifstream ifs(argv[1]);
  TextQuery tq(ifs);
  Query q = Query("fiery") & Query("bird") | Query("wind");
  cout << q << endl;
  auto result = q.eval(tq);
  print(cout, result);
}

15.42.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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
class Query_base
{
  friend class Query;
protected:
  using line_no = TextQuery::line_no;
  Query_base() {cout << "Query_base::Query_base()" << endl;}
  virtual ~Query_base() = default;
private:
  virtual QueryResult eval(const TextQuery &) const = 0;
  virtual string rep() const = 0;
};

class Query
{
  friend Query operator~(const Query &);
  friend Query operator|(const Query &, const Query &);
  friend Query operator&(const Query &, const Query &);
public:
  Query(const string &);
  QueryResult eval(const TextQuery &t) const {return q->eval(t);}
  string rep() const {cout << "Query::rep" << endl; return q->rep();}
private:
 Query(shared_ptr<Query_base> query): q(query) {cout << "Query::Query(shared_ptr)" << endl;}
  shared_ptr<Query_base> q;
};

class WordQuery: public Query_base
{
  friend class Query;
 WordQuery(const string &s): query_word(s) {cout << "WordQuery::WordQuery(const string &)" << endl;}
  QueryResult eval(const TextQuery &t) const
  {return t.query(query_word);}
  string rep() const {cout << "WordQuery::rep" << endl; return query_word;}
  string query_word;
};

inline Query::Query(const string &s): q(new WordQuery(s)) {cout << "Query::Query(const string &)" << endl;}

class BinaryQuery: public Query_base
{
 protected:
 BinaryQuery(const Query &l, const Query &r, string s):
  lhs(l), rhs(r), opSym(s) {cout << "BinaryQuery::BinaryQuery" << endl;}
  string rep() const
  {
    cout << "BinaryQuery::rep" << endl;
    return "(" + lhs.rep() + " "
      + opSym + " "
      + rhs.rep() + ")";
  }
  Query lhs, rhs;
  string opSym;
};

class AndQuery: public BinaryQuery
{
  friend Query operator&(const Query &, const Query &);
 AndQuery(const Query &left, const Query &right):
  BinaryQuery(left, right, "&") {cout << "AndQuery::AndQuery" << endl;}
  QueryResult eval(const TextQuery &) const;
};

inline Query operator&(const Query &lhs, const Query &rhs)
{
  return shared_ptr<Query_base>(new AndQuery(lhs, rhs));
}

QueryResult
AndQuery::eval(const TextQuery &text) const
{
  auto left = lhs.eval(text), right = rhs.eval(text);
  auto ret_lines = make_shared<set<line_no>>();
  set_intersection(left.begin(), left.end(),
		  right.begin(), right.end(),
		  inserter(*ret_lines, ret_lines->begin()));
  return QueryResult(rep(), ret_lines, left.get_file());
}

class OrQuery: public BinaryQuery
{
  friend Query operator|(const Query &, const Query &);
 OrQuery(const Query &left, const Query &right):
  BinaryQuery(left, right, "|") {cout << "OrQUery::OrQuery" << endl;}
  QueryResult eval(const TextQuery &) const;
};

inline Query operator|(const Query &lhs, const Query &rhs)
{
  return shared_ptr<Query_base>(new OrQuery(lhs, rhs));
}

ostream &operator<<(ostream &os, const Query &query)
{
  return os << query.rep();
}

QueryResult OrQuery::eval(const TextQuery &text) const
{
  auto right = rhs.eval(text), left = lhs.eval(text);
  auto ret_lines = make_shared<set<line_no>>(left.begin(), left.end());
  ret_lines->insert(right.begin(), right.end());
  return QueryResult(rep(), ret_lines, left.get_file());
}

class NotQuery: public Query_base
{
  friend Query operator~(const Query &);
 NotQuery(const Query &q): query(q) {}
  string rep() const {return "~(" + query.rep() + ")";}
  QueryResult eval(const TextQuery &) const;
  Query query;
};

inline Query operator~(const Query &operand)
{
  return shared_ptr<Query_base>(new NotQuery(operand));
}

QueryResult
NotQuery::eval(const TextQuery &text) const
{
  auto result = query.eval(text);
  auto ret_lines = make_shared<set<line_no>>();
  auto beg = result.begin(), end = result.end();
  auto sz = result.get_file()->size();
  for(size_t n = 0; n != sz; ++n)
    {
      if(beg == end || *beg != n)
	ret_lines->insert(n);
      else if(beg != end)
	++beg;
    }
  return QueryResult(rep(), ret_lines, result.get_file());
}
#endif

TextQuery-1542.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
#ifndef TEXTQUERY1542_H
#define TEXTQUERY1542_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;
};
istream &get_sentence(istream &is, string &s)
{
  s = "";
  string word;
  while(is >> word)
    {
      char symbol = word[word.size() - 1];
      if(ispunct(symbol))
      {
	s = s + word;
	break;
      }
      else
      s = s + word + " ";
    }
  return is;
}
TextQuery::TextQuery(ifstream &infile):
sp1(new vector<string>)
{
  line_no line_number = 0;
  string sentence;
  while(get_sentence(infile, sentence))
    {
      // store sentence in vector<string>
      sp1->push_back(sentence);
      // scan through the sentence
      istringstream ist(sentence);
      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(sentence " << 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