Skip to content

Commit 9df6f6e

Browse files
author
Jaege
committed
Add soultions to first half of exercises in chapter 15.
1 parent 9eccc57 commit 9df6f6e

23 files changed

+713
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ This repo is the solutions to exercises in book [_C++ Primer_ (5th Edition)](htt
139139
[41](ch14/14.41.md)|[42](ch14/14.42.cpp)|[43](ch14/14.43.cpp)|[44](ch14/14.44.cpp)|[45](ch14/14.45.cpp)|[46](ch14/14.46.md)|[47](ch14/14.47.md)|[48](ch14/14.48.md)|[49](ch14/14.49.md)|[50](ch14/14.50.cpp)|
140140
[51](ch14/14.51.cpp)|[52](ch14/14.52.cpp)|[53](ch14/14.53.cpp)
141141

142-
<!---
143142
#### Chapter 15 [Object-Oriented Programming](ch15)
144143

145144
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10
@@ -150,6 +149,7 @@ This repo is the solutions to exercises in book [_C++ Primer_ (5th Edition)](htt
150149
[31](ch15/15.31.cpp)|[32](ch15/15.32.cpp)|[33](ch15/15.33.cpp)|[34](ch15/15.34.cpp)|[35](ch15/15.35.cpp)|[36](ch15/15.36.cpp)|[37](ch15/15.37.cpp)|[38](ch15/15.38.cpp)|[39](ch15/15.39.cpp)|[40](ch15/15.40.cpp)|
151150
[41](ch15/15.41.cpp)|[42](ch15/15.42.cpp)
152151

152+
<!---
153153
#### Chapter 16 [Templates and Generic Programming](ch16)
154154
155155
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10

ch15/15.1.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
A virtual member is a function that the base class expects its derived classes to override. When we call a virtual function through a pointer or reference to base class, the call will be dynamically bound to the virtual function of the dynamic type of the pointer or reference.

ch15/15.10.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`ifstream` is inherited from `istream`. Thus, we can use objects of type `ifstream` as if they were `istream` objects. The parameter of `read` function is a reference to base type `istream`, the automatic derived-to-base conversion applies when we pass a derived type `ifstream` to that function. Inside `read` function, we can use the `istream` subpart in the `ifstream` object through the reference.

ch15/15.11.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// based on ex15.7
2+
#include <string>
3+
#include <iostream>
4+
5+
class Quote {
6+
public:
7+
Quote() = default;
8+
Quote(const std::string &book, double pri)
9+
: bookNo(book), price(pri) { }
10+
11+
virtual ~Quote() = default;
12+
13+
std::string isbn() const { return bookNo; }
14+
virtual double net_price(std::size_t n) const { return n * price; }
15+
16+
virtual std::ostream &debug(std::ostream &os) const {
17+
os << "Quote::bookNo " << bookNo << " Quote::price " << price;
18+
return os;
19+
}
20+
21+
protected:
22+
double price = 0.0;
23+
24+
private:
25+
std::string bookNo;
26+
};
27+
28+
class Bulk_quote : public Quote {
29+
public:
30+
Bulk_quote() = default;
31+
Bulk_quote(const std::string &book, double pri,
32+
std::size_t qty, double disc)
33+
: Quote(book, pri), min_qty(qty), discount(disc) { }
34+
35+
double net_price(std::size_t n) const override {
36+
if (n >= min_qty)
37+
return n * price * (1 - discount);
38+
else
39+
return n * price;
40+
}
41+
42+
std::ostream &debug(std::ostream &os) const override {
43+
Quote::debug(os) << " Bulk_quote::min_qty " << min_qty
44+
<< " Bulk_quote::discount " << discount;
45+
return os;
46+
}
47+
48+
private:
49+
std::size_t min_qty = 0;
50+
double discount = 0.0;
51+
};
52+
53+
class Limit_quote : public Quote {
54+
public:
55+
Limit_quote() = default;
56+
Limit_quote(const std::string &book, double pri,
57+
std::size_t qty, double disc)
58+
: Quote(book, pri), max_qty(qty), discount(disc) { }
59+
60+
double net_price(std::size_t n) const override {
61+
if (n <= max_qty)
62+
return n * price * (1 - discount);
63+
else
64+
return (n - max_qty) * price + max_qty * price * (1 - discount);
65+
}
66+
67+
std::ostream &debug(std::ostream &os) const override {
68+
Quote::debug(os) << " Limit_quote::max_qty " << max_qty
69+
<< " Limit_quote::discount " << discount;
70+
return os;
71+
}
72+
73+
private:
74+
std::size_t max_qty = 0;
75+
double discount = 0.0;
76+
};
77+
78+
double print_total(std::ostream &os, const Quote &item, size_t n) {
79+
double ret = item.net_price(n);
80+
os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret
81+
<< std::endl;
82+
return ret;
83+
}
84+
85+
int main() {
86+
Quote basic("abc", 5.5);
87+
Bulk_quote bulk("abc", 5.5, 10, 0.2);
88+
Limit_quote limit("abc", 5.5, 5, 0.2);
89+
90+
print_total(std::cout, basic, 20);
91+
print_total(std::cout, bulk, 20);
92+
print_total(std::cout, limit, 20);
93+
94+
basic.debug(std::cout) << std::endl;
95+
bulk.debug(std::cout) << std::endl;
96+
limit.debug(std::cout) << std::endl;
97+
98+
return 0;
99+
}

ch15/15.12.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
Both `override` and `final` can appear only in declaration in a virtual function. And both key words can be used in the same function declaration, but whether it is useful to use them both depends on situations.
2+
3+
Take the following code as an example:
4+
5+
#include <iostream>
6+
using std::cout; using std::endl;
7+
8+
struct B {
9+
virtual void f1() { cout << "B::f1() "; }
10+
virtual void f2() { cout << "B::f2() "; }
11+
virtual void f3() { cout << "B::f3() "; }
12+
virtual void f6() final { cout << "B::f6() "; }
13+
void f7() { cout << "B::f7() "; }
14+
void f8() { cout << "B::f8() "; }
15+
void f9() { cout << "B::f9() "; }
16+
};
17+
18+
struct D : B {
19+
void f1() override { cout << "D::f1() "; }
20+
void f2() final { cout << "D::f2() "; }
21+
void f3() override final { cout << "D::f3() "; } // need not have override
22+
// should have override, otherwise add new virtual function
23+
virtual void f4() final { cout << "D::f4() "; }
24+
//virtual void f5() override final; // Error, no virtual function in base class
25+
//void f6(); // Error, override a final virtual function
26+
void f7() { cout << "D::f7() "; }
27+
virtual void f8() { cout << "D::f8() "; }
28+
//void f9() override; // Error, override a nonvirtual function
29+
};
30+
31+
int main() {
32+
B b; D d;
33+
B *bp = &b, *bd = &d; D *dp = &d;
34+
bp->f1(); bp->f2(); bp->f3(); bp->f6(); bp->f7(); bp->f8(); bp->f9(); cout << endl;
35+
bd->f1(); bd->f2(); bd->f3(); bd->f6(); bd->f7(); bd->f8(); bd->f9(); cout << endl;
36+
dp->f1(); dp->f2(); dp->f3(); dp->f6(); dp->f7(); dp->f8(); dp->f9(); cout << endl;
37+
return 0;
38+
}
39+
40+
The output is
41+
42+
B::f1() B::f2() B::f3() B::f6() B::f7() B::f8() B::f9()
43+
D::f1() D::f2() D::f3() B::f6() B::f7() B::f8() B::f9()
44+
D::f1() D::f2() D::f3() B::f6() D::f7() D::f8() B::f9()
45+
46+
1. Compare `f1()` and `f6()`. We know that `override` and `final` is indepent sematically.
47+
48+
- `override` means the function is overriding a virtual function in its base class. See `f1()` and `f3()`.
49+
- `final` means the function cannot be overrided by its derived class. (But the function itself need not override a base class virtual function.) See `f6()` and `f4()`.
50+
51+
2. Compare `f2()` and `f3()`. We know that if a member function is declared without `virtual` and with `final`, it means that it already override a virtual function in base class. In this case, the key word `override` is redundant.
52+
53+
3. Compare `f4()` and `f5()`. We know that if a member function is declared with `virtual`and if it is not the *first* virtual function in inheritance hierarchy, then we should use `override` to specify the override relationship. Otherwise, we may accidentally add new virtual function in derived class.
54+
55+
4. Compare `f1()` and `f7()`. We know that any member function, not just virtual ones, can be overridden in derived class. What `virtual` specifies is *polymorphism*, which means the decision as to which function to run is delayed until run time instead of compile time. (This should be avoid in practice.)
56+
57+
5. Compare `f7()` and `f8()`. We know that we can even override a base class function and make it a new virtual one. (Which means any member function `f8()` of class derived from `D` will be virtual.) (This should be avoid in practice too.)
58+
59+
6. Compare `f7()` and `f9()`. We know that `override` can help us find the error when we want to override a virtual function in derived class while forgot to add key word `virtual` in base class.
60+
61+
**In conclusion**, the best practice in my own view is:
62+
63+
- *only* use `virtual` in declaration of the *first* virtual function in base class;
64+
- always use `override` to specify override virtual function in derived class, unless `final` is also specified.

ch15/15.13.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The derived virtual function `print` try to call the base version in its function body, but without the scope operator, the call will be resolved at run time as a call to the derived version itself, resulting in an infinite recursion.
2+
3+
To fix this problem, we need to prefix the scope operator to the call like `base::print(os);`.

ch15/15.14.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include <string>
2+
#include <iostream>
3+
using std::string;
4+
using std::ostream;
5+
6+
class base {
7+
public:
8+
string name() { return basename; }
9+
virtual void print(ostream &os) { os << basename; }
10+
private:
11+
string basename;
12+
};
13+
14+
class derived : public base {
15+
public:
16+
void print(ostream &os) { base::print(os); os << " " << i; }
17+
private:
18+
int i = 0;
19+
};
20+
21+
int main() {
22+
base bobj; base *bp1 = &bobj; base &br1 = bobj;
23+
derived dobj; base *bp2 = &dobj; base &br2 = dobj;
24+
// (a) base::print();
25+
bobj.print(std::cout);
26+
// (b) derived::print();
27+
dobj.print(std::cout);
28+
// (c) base::name();
29+
bp1->name();
30+
// (d) base::name();
31+
bp2->name();
32+
// (e) base::print();
33+
br1.print(std::cout);
34+
// (f) derived::print();
35+
br2.print(std::cout);
36+
37+
return 0;
38+
}

ch15/15.15.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// based on ex15.11
2+
#include <string>
3+
#include <iostream>
4+
5+
class Quote {
6+
public:
7+
Quote() = default;
8+
Quote(const std::string &book, double pri)
9+
: bookNo(book), price(pri) { }
10+
11+
virtual ~Quote() = default;
12+
13+
std::string isbn() const { return bookNo; }
14+
virtual double net_price(std::size_t n) const { return n * price; }
15+
16+
virtual std::ostream &debug(std::ostream &os) const {
17+
os << "Quote::bookNo " << bookNo << " Quote::price " << price;
18+
return os;
19+
}
20+
21+
protected:
22+
double price = 0.0;
23+
24+
private:
25+
std::string bookNo;
26+
};
27+
28+
class Disc_quote : public Quote {
29+
public:
30+
Disc_quote() = default;
31+
Disc_quote(const std::string &book, double pri,
32+
std::size_t qty, double disc)
33+
: Quote(book, pri), quantity(qty), discount(disc) { }
34+
35+
double net_price(std::size_t n) const = 0;
36+
37+
std::ostream &debug(std::ostream &os) const override {
38+
Quote::debug(os) << " Disc_quote::quantity " << quantity
39+
<< " Disc_quote::discount " << discount;
40+
return os;
41+
}
42+
43+
protected:
44+
std::size_t quantity = 0;
45+
double discount = 0.0;
46+
};
47+
48+
class Bulk_quote : public Disc_quote {
49+
public:
50+
Bulk_quote() = default;
51+
Bulk_quote(const std::string &book, double pri, std::size_t qty, double disc)
52+
: Disc_quote(book, pri, qty, disc) { }
53+
54+
double net_price(std::size_t n) const override {
55+
if (n >= quantity)
56+
return n * price * (1 - discount);
57+
else
58+
return n * price;
59+
}
60+
};
61+
62+
class Limit_quote : public Disc_quote {
63+
public:
64+
Limit_quote() = default;
65+
Limit_quote(const std::string &book, double pri, std::size_t qty, double disc)
66+
: Disc_quote(book, pri, qty, disc) { }
67+
68+
double net_price(std::size_t n) const override {
69+
if (n <= quantity)
70+
return n * price * (1 - discount);
71+
else
72+
return (n - quantity) * price + quantity * price * (1 - discount);
73+
}
74+
};
75+
76+
double print_total(std::ostream &os, const Quote &item, size_t n) {
77+
double ret = item.net_price(n);
78+
os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret
79+
<< std::endl;
80+
return ret;
81+
}
82+
83+
int main() {
84+
Quote basic("abc", 5.5);
85+
Bulk_quote bulk("abc", 5.5, 10, 0.2);
86+
Limit_quote limit("abc", 5.5, 5, 0.2);
87+
88+
print_total(std::cout, basic, 20);
89+
print_total(std::cout, bulk, 20);
90+
print_total(std::cout, limit, 20);
91+
92+
basic.debug(std::cout) << std::endl;
93+
bulk.debug(std::cout) << std::endl;
94+
limit.debug(std::cout) << std::endl;
95+
96+
return 0;
97+
}

ch15/15.16.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
See [ex15.15](15.15.cpp).

0 commit comments

Comments
 (0)