(放两首喽,纪念一下一晚看完)

孤独摇滚 山田凉

Chapter 15. Object-Oriented Programming

Exercise 15.1

What is a virtual member?

A member function should be dynamically bound by preceding its declaration.

A virtual member in a base class expects its derived class define its own version. In particular base classes ordinarily should define a virtual destructor, even if it does no work.

虚成员函数(virtual member function)是在类的声明中使用关键字 virtual 声明的成员函数。虚成员函数允许派生类(derived class)覆盖(override)基类(base class)中的同名函数,实现多态性(polymorphism)。

当派生类中定义了与基类中虚函数同名同参数列表的函数时,这个函数会覆盖基类中的虚函数,即使它们的实现不同。在运行时,通过基类指针或引用调用虚函数时,程序会根据指针或引用所指向的实际对象的类型来动态绑定(dynamically bind)到正确的函数实现,从而实现多态性的效果。

通常情况下,如果基类中存在虚函数,建议将析构函数也声明为虚函数。这是因为在使用基类指针或引用删除派生类对象时,如果基类的析构函数不是虚函数,可能导致只会调用基类的析构函数而不会调用派生类的析构函数,从而造成内存泄漏或行为未定义的情况。声明基类析构函数为虚函数可以确保在使用多态性时正确地释放派生类对象的资源。

Exercise 15.2

How does the protected access specifier differ from private?

base class members:

  • private:: base class itself and friends can access
  • protected: base class itself, friends and derived classes can access

访问控制修饰符 protectedprivate 的区别在于:

基类成员:

  • private::只有基类自身和友元可以访问。
  • protected::基类自身、友元以及派生类都可以访问。

Exercise 15.3

Define your own versions of the Quote class and the print_total function.

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
/*
=================================================================================

C++ Primer 5th Exercise Answer Source Code
Copyright (C) 2014-2015 github.com/pezy/Cpp-Primer

Quote class and print_total function

If you have questions, try to connect with me: pezy<urbancpz@gmail.com>

=================================================================================
*/

#ifndef CP5_EX15_03_QUOTE_H_
#define CP5_EX15_03_QUOTE_H_

#include <string>
#include <iostream>

inline namespace EX03 {

using std::string;
using std::ostream; using std::endl;

class Quote {
public:
Quote() = default;
Quote(string const& b, double p) : bookNo(b), price(p) { }

string isbn() const { return bookNo; }
virtual double net_price(size_t n) const { return n * price; }

virtual ~Quote() = default;
private:
string bookNo;
protected:
double price = 0.0;
};

double print_total(ostream& os, Quote const& item, size_t n) {
double ret = item.net_price(n);
os << "ISBN: " << item.isbn() << " # sold: " << n << " total due: " << ret << endl;
return ret;
}

}

#endif // CP5_EX15_03_QUOTE_H_

Exercise 15.4

Which of the following declarations, if any, are incorrect? Explain why.

1
2
3
4
class Base { ... };
(a) class Derived : public Derived { ... };
(b) class Derived : private Base { ... };
(c) class Derived : public Base;
    1. incorrect, derive from itself
    1. correct.
    1. incorrect, A derived class is declared like any other class. The declaration contains the class name but does not include its derivation list

提供的声明中:

  1. class Derived : public Derived { ... }; 是不正确的。该声明尝试将类 Derived 派生自身,这是不允许的。一个类不能是其自身的基类。

  2. class Derived : private Base { ... }; 是正确的。它声明了一个类 Derived,从基类 Base 私有派生而来。这意味着 Base 的公共和保护成员成为了 Derived 的私有成员。

  3. class Derived : public Base; 是不正确的。这种语法不是用于声明派生类的。在声明派生类时,你需要在冒号后面指定基类,然后是访问说明符。在这种情况下,缺少了派生类的主体部分。

Exercise 15.5

Define your own version of the Bulk_quote class.

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
/*
=================================================================================

C++ Primer 5th Exercise Answer Source Code
Copyright (C) 2014-2015 github.com/pezy/Cpp-Primer

Bulk_quote class

If you have questions, try to connect with me: pezy<urbancpz@gmail.com>

=================================================================================
*/

#ifndef CP5_EX15_05_BULK_QUOTE_H_
#define CP5_EX15_05_BULK_QUOTE_H_

#include "ex15_03_Quote.h"
#include <string>

namespace EX05 {
using std::string;
using namespace EX03;

class Bulk_quote : public Quote {
public:
Bulk_quote() = default;
Bulk_quote(string const& book, double p, size_t qty, double disc) : Quote(book, p), min_qty(qty), discount(disc) {}

virtual double net_price(size_t cnt) const override {
if (cnt >= min_qty) return cnt * (1 - discount) * price;
else return cnt * price;
}
protected:
size_t min_qty = 0;
double discount = 0.0;
};
}

#endif // CP5_EX15_05_BULK_QUOTE_H_

这段代码定义了一个名为 Bulk_quote 的类,它是 Quote 类的派生类。Bulk_quote 类添加了两个额外的成员变量 min_qtydiscount,用于表示最小购买数量和折扣率。

Bulk_quote 类中,重载了基类 Quotenet_price 函数。如果购买数量达到或超过 min_qty,则应用折扣;否则按原价计算。

Exercise 15.7

Define a class that implements a limited discount strategy, which applies a discount to books purchased up to a given limit. If the number of copies exceeds that limit, the normal price applies to those purchased beyond the limit.

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
/*
=================================================================================

C++ Primer 5th Exercise Answer Source Code
Copyright (C) 2014-2015 github.com/pezy/Cpp-Primer

Limit_quote class

If you have questions, try to connect with me: pezy<urbancpz@gmail.com>

=================================================================================
*/

#ifndef CP5_EX15_07_LIMIT_QUOTE_H_
#define CP5_EX15_07_LIMIT_QUOTE_H_

#include "ex15_05_Bulk_quote.h"
#include <string>

namespace EX07 {
using namespace EX05;
using std::string;

class Limit_quote : public Bulk_quote {
public:
Limit_quote() = default;
Limit_quote(string const& book, double p, size_t min, size_t max, double dist) : Bulk_quote(book, p, min, dist), max_qty(max) {}

double net_price(size_t cnt) const override {
if (cnt > max_qty) return max_qty * (1 - discount) * price + (cnt - max_qty) * price;
else if (cnt >= min_qty) return cnt * (1 - discount) * price;
else return cnt * price;
}

private:
size_t max_qty = 0;
};
}

#endif // LIMIT_QUOTE_H_

Exercise 15.8

Define static type and dynamic type.

  • static type: Type with which a variable is defined or that an expression yields. Static type is known at compile time.
  • dynamic type: Type of an object at run time. The dynamic type of an object to which a reference refers or to which a pointer points may differ from the static type of the reference or pointer.
  • 静态类型:变量定义时的类型,或表达式生成的类型。静态类型在编译时就已知。
  • 动态类型:对象在运行时的类型。引用所引用的对象或指针所指向的对象的动态类型可能与引用或指针的静态类型不同。

Exercise 15.9

When is it possible for an expression’s static type to differ from its dynamic type? Give three examples in which the static and dynamic type differ.

A pointer or reference to a base-class type can refer to an to object of derived type. In such cases the static type is reference (or pointer) to base, but the dynamic type is reference (or pointer) to derived.

Anything like this can be an example.

在什么情况下,表达式的静态类型可以与动态类型不同?给出三个静态类型与动态类型不同的示例。

当指针或引用指向基类类型的对象时,可以引用到派生类型的对象。在这种情况下,静态类型是指向基类的引用(或指针),而动态类型是指向派生类的引用(或指针)。

类似这样的情况都可以作为示例。

Exercise 15.10

Recalling the discussion from 8.1 (p. 311), explain how the program on page 317 that passed an ifstream to the Sales_data read function works.

the function takes a istream from which ifstream is derived. Hence the ifstream object "is a" istream ,which is why it works.

回顾一下第8.1节(第311页)的讨论,解释一下317页上的程序是如何将ifstream传递给Sales_data read函数的。

该函数接受一个istream,而ifstream是从istream派生出来的。因此,ifstream对象“是”一个istream,这就是为什么它能够工作的原因。

Exercise 15.12

Is it ever useful to declare a member function as both override and final? Why or why not?

声明成员函数既为override又为final有什么用处吗?为什么?声明成员函数既为override又为final有什么用处吗?为什么?

Sure. override means overriding the same name virtual function in base class. final means preventing any overriding this virtual function by any derived classes that are more lower at the hierarchy.

当然可以。override 表示在派生类中覆盖相同名称的基类虚函数。final 表示阻止任何更低层次的派生类对该虚函数进行覆盖。

Exercise 15.13

Given the following classes, explain each print function:

1
2
3
4
5
6
7
8
9
10
11
12
13
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; }
private:
int i;
};

If there is a problem in this code, how would you fix it?

The print in derived::print wanted to call the print from the base class. However, the class scope base:: was omitted. As a result, it will cause an infinite recursion.

Fixed:

1
void print(ostream &os) { base::print(os); os << " " << i; }

在derived::print中,想要调用base类的print函数。然而,忽略了类作用域base::,导致了无限递归。

Exercise 15.14

Given the classes from the previous exercise and the following objects, determine which function is called at run time:

1
2
3
4
5
6
7
8
base bobj; base *bp1 = &bobj; base &br1 = bobj;
derived dobj; base *bp2 = &dobj; base &br2 = dobj;
(a) bobj.print(); // base::print()
(b) dobj.print(); // derived::print()
(c) bp1->name(); // base::name()
(d) bp2->name(); // base::name()
(e) br1.print(); // base::print()
(f) br2.print(); // derived::print()

e and f are called at run time.

Exercise 15.15

Define your own versions of Disc_quote and Bulk_quote.

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
/*
=================================================================================

C++ Primer 5th Exercise Answer Source Code
Copyright (C) 2014-2015 github.com/pezy/Cpp-Primer

Disc_quote

If you have questions, try to connect with me: pezy<urbancpz@gmail.com>

=================================================================================
*/

#ifndef CP5_EX15_15_DISC_QUOTE_H_
#define CP5_EX15_15_DISC_QUOTE_H_

#include "ex15_03_Quote.h"
#include <string>

inline namespace EX15 {

using std::string;

class Disc_quote : public EX03::Quote {
public:
Disc_quote() = default;
Disc_quote(string const& b, double p, size_t q, double d) : EX03::Quote(b, p), quantity(q), discount(d){ }
virtual double net_price(size_t) const = 0;
protected:
size_t quantity = 0;
double discount = 0.0;
};

}

#endif // CP5_EX15_15_DISC_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
=================================================================================

C++ Primer 5th Exercise Answer Source Code
Copyright (C) 2014-2015 github.com/pezy/Cpp-Primer

Disc_quote

If you have questions, try to connect with me: pezy<urbancpz@gmail.com>

=================================================================================
*/

#ifndef CP5_EX15_15_BULK_QUOTE_H_
#define CP5_EX15_15_BULK_QUOTE_H_

#include "ex15_15_Disc_quote.h"
#include <string>

inline namespace EX15 {

using std::string;

class Bulk_quote : public Disc_quote {
public:
Bulk_quote() = default;
Bulk_quote(string const& book, double price, size_t qty, double disc) : Disc_quote(book, price, qty, disc) { }
virtual double net_price(std::size_t cnt) const override {
if (cnt >= quantity) return cnt * (1 - discount) * price;
else return cnt * price;
}
};

}

#endif // CP5_EX15_15_BULK_QUOTE_H_

Exercise 15.16

Rewrite the class representing a limited discount strategy, which you wrote for the exercises in 15.2.2 (p. 601), to inherit from Disc_quote.

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
/*
=================================================================================

C++ Primer 5th Exercise Answer Source Code
Copyright (C) 2014-2015 github.com/pezy/Cpp-Primer

Limit_quote class
inherit from Disc_quote

If you have questions, try to connect with me: pezy<urbancpz@gmail.com>

=================================================================================
*/

#ifndef CP5_EX15_16_LIMIT_QUOTE_H_
#define CP5_EX15_16_LIMIT_QUOTE_H_

#include "ex15_15_Disc_quote.h"
#include <string>

namespace EX16 {
using std::string;
using std::cout; using std::endl;
using namespace EX15;

class Limit_quote : public Disc_quote {
public:
Limit_quote() = default;
Limit_quote(string const& book, double p, size_t min, size_t max, double dist) : Disc_quote(book, p, min, dist), max_qty(max) {}

double net_price(size_t cnt) const final override {
if (cnt > max_qty) return max_qty * (1 - discount) * price + (cnt - max_qty) * price;
else if (cnt >= quantity) return cnt * (1 - discount) * price;
else return cnt * price;
}

private:
size_t max_qty = 0;
};
}

#endif // CP5_EX15_16_LIMIT_QUOTE_H_

Exercise 15.17

Try to define an object of type Disc_quote and see what errors you get from the compiler.

1
2
note:   because the following virtual functions are pure within 'ch15::EX15::Disc_quote':
virtual double net_price(size_t) const = 0;

在尝试定义类型为 Disc_quote 的对象时,编译器会报错,因为 Disc_quote 类中的虚函数 net_price(size_t) 是一个纯虚函数(pure virtual function),没有在类中提供具体的实现。纯虚函数必须在派生类中被重写(override)以提供具体的实现,否则派生类也将成为抽象类,无法实例化对象。

错误消息中提到了这一点,指示 Disc_quote 类中的 net_price(size_t) 函数是纯虚函数,需要在派生类中提供具体的实现。

Exercise 15.21

Choose one of the following general abstractions containing a family of types (or choose one of your own). Organize the types into an inheritance hierarchy:

    1. Graphical file formats (such as gif, tiff, jpeg, bmp)
    1. Geometric primitives (such as box, circle, sphere, cone)
    1. C++ language types (such as class, function, member function) 选择以下包含一组类型的通用抽象(或选择您自己的抽象)。将这些类型组织成一个继承层次结构:
  1. 图形文件格式(如 gif、tiff、jpeg、bmp)
  2. 几何原语(如盒子、圆、球、圆锥)
  3. C++ 语言类型(如类、函数、成员函数)
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
/*
=================================================================================

C++ Primer 5th Exercise Answer Source Code
Copyright (C) 2014-2015 github.com/pezy/Cpp-Primer

Geomtric Primitives
box, circle, sphere, cone

If you have questions, try to connect with me: pezy<urbancpz@gmail.com>

=================================================================================
*/

#ifndef CP5_EX15_21_GEOMTRIC_PRIMITIVES_H_
#define CP5_EX15_21_GEOMTRIC_PRIMITIVES_H_

inline namespace EX21 {

static const float PI = 3.14159f;

class Shape {
public:
virtual const char* shape_name() = 0;
virtual void resize_by_percentage(float pct) = 0;
virtual ~Shape() {}
};

class Shape_2D : public Shape {
public:
Shape_2D() = default;
Shape_2D(float x, float y) : x_(x), y_(y) {}

virtual float area() const = 0;
virtual float diameter() const = 0;
virtual float circumference() const = 0;

virtual ~Shape_2D() override {}

protected:
float x_ = 0.f;
float y_ = 0.f;
};

class Shape_3D : public Shape {
public:
Shape_3D() = default;
Shape_3D(float x, float y, float z) : x_(x), y_(y), z_(z) {}

virtual float volume() const = 0;

virtual ~Shape_3D() override {}

protected:
float x_ = 0.f;
float y_ = 0.f;
float z_ = 0.f;
};

class Box : public Shape_3D {
public:
Box() = default;
explicit Box(float width) : half_len_x_(width * 0.5f), half_len_y_(width * 0.5f), half_len_z_(width * 0.5f) {}
Box(float center_x, float center_y, float center_z, float len_x, float len_y, float len_z) : Shape_3D(center_x, center_y, center_z),
half_len_x_(len_x * 0.5f), half_len_y_(len_y * 0.5f), half_len_z_(len_z * 0.5f) {}

inline virtual const char* shape_name() override { return "Box"; }
inline virtual void resize_by_percentage(float pct) override { half_len_x_ *= pct; half_len_y_ *= pct; half_len_z_ *= pct; }

inline float volume() const override { return half_len_x_ * half_len_y_ * half_len_z_ * 8; }

virtual ~Box() override {}

protected:
float half_len_x_ = 0.5f;
float half_len_y_ = 0.5f;
float half_len_z_ = 0.5f;
};

class Circle : public Shape_2D {
public:
Circle() = default;
explicit Circle(float radius) : radius_(radius) {}
Circle(float center_x, float center_y, float radius) : Shape_2D(center_x, center_y), radius_(radius) {}

inline virtual float area() const override { return PI * radius_ * radius_; }
inline virtual float diameter() const override { return 2 * radius_; }
inline virtual float circumference() const override { return 2 * PI * radius_; }

inline virtual const char* shape_name() override { return "Circle"; }
inline virtual void resize_by_percentage(float pct) override { radius_ *= pct; }

virtual ~Circle() override {}

protected:
float radius_ = 1.f;
};

class Sphere : public Shape_3D {
public:
Sphere() = default;
explicit Sphere(float radius) : radius_(radius) {}
Sphere(float center_x, float center_y, float center_z, float radius) : Shape_3D(center_x, center_y, center_z), radius_(radius) {}

inline virtual const char* shape_name() override { return "Sphere"; }
inline virtual void resize_by_percentage(float pct) override { radius_ *= pct; }

inline float volume() const override { return 4 * PI * radius_ * radius_ * radius_ / 3; }

virtual ~Sphere() override {}

protected:
float radius_ = 1.f;
};

class Cone : public Shape_3D {
public:
Cone() = default;
Cone(float radius, float height) : radius_(radius), height_(height) {}
Cone(float center_x, float center_y, float center_z, float radius, float height) : Shape_3D(center_x, center_y, center_z), radius_(radius), height_(height) {}

inline virtual const char* shape_name() override { return "Cone"; }
inline virtual void resize_by_percentage(float pct) override { radius_ *= pct; height_ *= pct; }

inline float volume() const override { return PI * radius_ * radius_ * height_ / 3; }

virtual ~Cone() override {}

protected:
float radius_ = 1.f;
float height_ = 1.f;
};
}

#endif // CP5_EX15_21_GEOMTRIC_PRIMITIVES_H_

Exercise 15.24

What kinds of classes need a virtual destructor? What operations must a virtual destructor perform?

Generally, a base class should define a virtual destructor.

The destructor needs to be virtual to allow objects in the inheritance hierarchy to be dynamically allocated.

什么样的类需要虚析构函数?虚析构函数必须执行哪些操作?

通常,一个基类应该定义一个虚析构函数。

虚析构函数需要在继承层次结构中允许对象进行动态分配

Exercise 15.25

Why did we define a default constructor for Disc_quote? What effect, if any, would removing that constructor have on the behavior of Bulk_quote?

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
#include <string>
using std::string;

class Quote {
public:
Quote() = default;
Quote(string const& b, double p) : bookNo(b), price(p) { }

string isbn() const { return bookNo; }
virtual double net_price(size_t n) const { return n * price; }

virtual ~Quote() = default;
private:
string bookNo;
protected:
double price = 0.0;
};

class Disc_quote : public Quote {
public:
//Disc_quote() = default;
Disc_quote(string const& b, double p, size_t q, double d) : Quote(b, p), quantity(q), discount(d){ }
virtual double net_price(size_t) const = 0;
protected:
size_t quantity = 0;
double discount = 0.0;
};

class Bulk_quote : public Disc_quote {
public:
Bulk_quote() = default;
Bulk_quote(string const& book, double price, size_t qty, double disc) : Disc_quote(book, price, qty, disc) { }
virtual double net_price(std::size_t cnt) const override {
if (cnt >= quantity) return cnt * (1 - discount) * price;
else return cnt * price;
}
};

int main()
{
Bulk_quote b_quote;
}

Without it, when building the upper codes, the compiler would conplain that:

1
2
3
4
5
6
error: use of deleted function 'Bulk_quote::Bulk_quote()'
Bulk_quote b_quote;
^
'Bulk_quote::Bulk_quote()' is implicitly deleted because the default definition would be ill-formed:
Bulk_quote() = default;
^

The reason is that a constructor taking 4 parameters has been defined, which prevented the compiler generate synthesized version default constructor. As a result, the default constructor of any class derived from it has been defined as deleted. Thus the default constructor must be defined explicitly so that the derived classes can call it when executing its default constructor.

我们为什么要为 Disc_quote 定义一个默认构造函数?如果删除该构造函数,对 Bulk_quote 的行为会有什么影响?

Disc_quote 中定义一个默认构造函数的目的是为了确保派生类(如 Bulk_quote)可以调用该构造函数,从而正确地构造对象。如果我们删除了 Disc_quote 中的默认构造函数,编译器将会为其生成一个合成的默认构造函数,但由于 Disc_quote 存在一个带参数的构造函数,这将导致合成的默认构造函数被删除。结果,派生类 Bulk_quote 将无法调用其默认构造函数,因为默认构造函数已经被删除了。

因此,为了确保派生类可以正确地构造对象,我们需要显式地定义 Disc_quote 的默认构造函数。

Exercise 15.26

Define the Quote and Bulk_quote copy-control members to do the same job as the synthesized versions. Give them and the other constructors print statements that identify which function is running. Write programs using these classes and predict what objects will be created and destroyed. Compare your predictions with the output and continue experimenting until your predictions are reliably correct.

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
/*
=================================================================================

C++ Primer 5th Exercise Answer Source Code
Copyright (C) 2014-2015 https://github.com/pezy/Cpp-Primer

Quote class
1. define copy-control members to do the same job as the synthesized versions.
2. Print function name to trace the running.

If you have questions, try to connect with me: pezy<urbancpz@gmail.com>

=================================================================================
*/

#ifndef CP5_EX15_26_QUOTE_H
#define CP5_EX15_26_QUOTE_H

#include <string>
#include <iostream>

namespace EX26 {
using std::string;
using std::cout; using std::endl;

class Quote {
public:
Quote() {
cout << "Quote Constructor" << endl;
}

Quote(const string &b, double p) : bookNo(b), price(p) {
cout << "Quote Constructor taking two parameters" << endl;
}

Quote(const Quote &rhs) : bookNo(rhs.bookNo), price(rhs.price) {
cout << "Quote Copy Constructor" << endl;
}

Quote& operator=(const Quote &rhs) {
cout << "Quote Copy assignment operator" << endl;
price = rhs.price;
bookNo = rhs.bookNo;
return *this;
}

Quote(Quote &&rhs) noexcept : bookNo(std::move(rhs.bookNo)), price(std::move(rhs.price)) {
cout << "Quote Move Constructor" << endl;
}

Quote& operator=(Quote &&rhs) noexcept {
cout << "Quote Move assignment operator" << endl;
bookNo = std::move(rhs.bookNo);
price = std::move(rhs.price);
return *this;
}

virtual ~Quote() {
cout << "Quote Destructor" << endl;
}

string isbn() const { return bookNo; }
virtual double net_price(size_t n) const { return n * price; }
private:
string bookNo;
protected:
double price = 0.0;
};

}

#endif //CP_EX15_26_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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/*
=================================================================================

C++ Primer 5th Exercise Answer Source Code
Copyright (C) 2014-2015 https://github.com/pezy/Cpp-Primer

Disc_quote class
1. define copy-control members to do the same job as the synthesized versions.
2. Print function name to trace the running.

If you have questions, try to connect with me: pezy<urbancpz@gmail.com>

=================================================================================
*/

#ifndef CP5_EX15_26_DISC_QUOTE_H
#define CP5_EX15_26_DISC_QUOTE_H

#include "ex15_26_Quote.h"

namespace EX26 {
class Bulk_quote : public Quote {
public:
Bulk_quote() {
cout << "Bulk_quote Constructor" << endl;
}

Bulk_quote(const string &b, double p, size_t q, double d) : Quote(b, p), min_qty(q), discount(d) {
cout << "Bulk_quote Constructor taking four paramters" << endl;
}

Bulk_quote(const Bulk_quote& rhs) : Quote(rhs), min_qty(rhs.min_qty), discount(rhs.discount) {
cout << "Bulk_quote Copy Constructor" << endl;
}

Bulk_quote& operator=(const Bulk_quote& rhs) {
cout << "Bulk_quote Copy assignment operator" << endl;
Quote::operator=(rhs);
min_qty = rhs.min_qty;
discount = rhs.discount;
return *this;
}

Bulk_quote(Bulk_quote &&rhs) noexcept : Quote(rhs), min_qty(std::move(rhs.min_qty)),
discount(std::move(rhs.discount)) {
cout << "Bulk_quote Move constructor" << endl;
}

Bulk_quote& operator=(Bulk_quote &&rhs) noexcept {
cout << "Bulk_quote Move assignment operator" << endl;
Quote::operator=(rhs);
min_qty = std::move(rhs.min_qty);
discount = std::move(rhs.discount);
return *this;
}

virtual ~Bulk_quote() {
cout << "Bulk_quote destructor" << endl;
}
virtual double net_price(size_t cnt) const override {
if (cnt >= min_qty) return cnt * (1 - discount) * price;
else return cnt * price;
}
protected:
size_t min_qty = 0;
double discount = 0.0;
};
}

#endif //CP5_EX15_26_DISC_QUOTE_H

Exercise 15.27

Redefine your Bulk_quote class to inherit its constructors.

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
/*
=================================================================================

C++ Primer 5th Exercise Answer Source Code
Copyright (C) 2014-2015 https://github.com/pezy/Cpp-Primer

Bulk_quote
inherited constructors from EX15::Disc_quote

If you have questions, try to connect with me: pezy<urbancpz@gmail.com>

=================================================================================
*/

#ifndef CP5_EX15_27_BULK_QUOTE_H
#define CP5_EX15_27_BULK_QUOTE_H

#include "ex15_15_Disc_quote.h"

inline namespace EX27 {

using std::string;

class Bulk_quote : public Disc_quote {
public:
using Disc_quote::Disc_quote;
virtual double net_price(std::size_t cnt) const override {
if (cnt >= quantity) return cnt * (1 - discount) * price;
else return cnt * price;
}
};

}

#endif // CP5_EX15_27_BULK_QUOTE_H

Exercise 15.28

Define a vector to hold Quote objects but put Bulk_quote objects into that vector. Compute the total net_price of all the elements in the vector.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <vector>
#include <numeric>
#include "ex15_27_Bulk_quote.h"

int main()
{
std::vector<Quote> vecQuote;

Bulk_quote bulk0("0-201-82470-1", 50, 5, 0.2);
Bulk_quote bulk1("0-201-82470-1", 50, 3, 0.5);

// total price should be:
std::cout << "bulk_quote's total: " << bulk0.net_price(5) + bulk1.net_price(5) << std::endl;

vecQuote.push_back(bulk0);
vecQuote.push_back(bulk1);

double total = std::accumulate(vecQuote.cbegin(), vecQuote.cend(),0.0, [](double ret, const Quote &obj){
return ret += obj.net_price(5);
});

// total price in the vector.
std::cout << "total in the vector: " << total << std::endl;
}

Output:

1
2
bulk_quote's total: 325
total in the vector: 500

Exercise 15.29

Repeat your program, but this time store shared_ptrs to objects of type Quote. Explain any discrepancy in the sum generated by the this version and the previous program. If there is no discrepancy, explain why there isn’t one.

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
#include <vector>
#include <numeric>
#include <memory>

#include "ex15_27_Bulk_quote.h"

int main()
{
std::vector<std::shared_ptr<Quote>> vecQuote;

std::shared_ptr<Bulk_quote> spBulk0 = std::make_shared<Bulk_quote>("0-201-82470-1", 50, 5, 0.2);
std::shared_ptr<Bulk_quote> spBulk1 = std::make_shared<Bulk_quote>("0-201-82470-1", 50, 3, 0.5);

// total price should be:
std::cout << "bulk_quote's total: " << spBulk0->net_price(5) + spBulk1->net_price(5) << std::endl;

vecQuote.push_back(spBulk0);
vecQuote.push_back(spBulk1);

double total = std::accumulate(vecQuote.cbegin(), vecQuote.cend(),0.0, [](double ret, std::shared_ptr<Quote> sp){
return ret += sp->net_price(5);
});

// total price in the vector.
std::cout << "total in the vector: " << total << std::endl;
}

Output:

1
2
bulk_quote's total: 325
total in the vector: 325

Exercise 15.30

Write your own version of the Basket class and use it to compute prices for the same transactions as you used in the previous exercises.

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
/*
=================================================================================

C++ Primer 5th Exercise Answer Source Code
Copyright (C) 2014-2015 https://github.com/pezy/Cpp-Primer

Basket.

If you have questions, try to connect with me: pezy<urbancpz@gmail.com>

=================================================================================
*/

#ifndef CP5_EX15_30_BASKET_H
#define CP5_EX15_30_BASKET_H

#include <memory>
#include <set>
#include "ex15_30_Quote_Bulk_quote.h"

namespace EX30 {
using std::shared_ptr;

class Basket {
public:
Basket() = default;
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())); }
inline double total_receipt(std::ostream&) const;
private:
static bool compare(const shared_ptr<Quote> &lhs, const shared_ptr<Quote> &rhs) {
return lhs->isbn() < rhs->isbn();
}
std::multiset<shared_ptr<Quote>, decltype(compare)*> items{compare};
};

inline double Basket::total_receipt(std::ostream &os) const {
auto 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 << std::endl;
return sum;
}
}

#endif //CP5_EX15_30_BASKET_H

这段代码实现了一个名为 Basket 的类,用于管理销售物品。它包括以下功能:

  • add_item(const Quote &sale):向购物篮中添加一个销售物品,参数为 Quote 类型的常量引用。
  • add_item(Quote &&sale):向购物篮中添加一个销售物品,参数为 Quote 类型的右值引用。
  • total_receipt(std::ostream&):计算购物篮中所有销售物品的总销售额,并打印每种物品的销售情况到给定的输出流中。

购物篮内部使用了 std::multiset 容器来存储销售物品的指针,并且提供了一个用于比较指针的比较函数 compare。在 total_receipt 函数中,使用 print_total 函数计算每种物品的销售额,并将结果累加到 sum 中,然后打印总销售额。

这个类的设计允许用户添加不同类型的销售物品,并根据每种物品的销售数量计算总销售额。

Exercise 15.32

What happens when an object of type Query is copied, moved, assigned, and destroyed?

  • copy: While being copied, the synthesized copy constructor is called. It copies the data member into the new object. Since in this case, the data member is a shared pointer, while copying, the corresponding shared pointer points to the same address and the use count from the both shared pointer becomes 2.
  • move: while being moved, the synthesized move constructor is called. It moves the data member into the new object. In this case, the shared pointer from the newly created object will point to the address to which the original shared pointer pointed. After the move operation, the use count of the shared pointer in the new object is 1, whereas the pointer from the original object becomes nullptr.
  • copy assignment: The synthesized copy assignment will be called. The outcome of this operation is identical with the copy operation.
  • move assignment: The synthesized move assignment will be called. The rest is the same as the move operation.
  • destroy: The synthesized destructor will be called. It will call the destructor of shared_ptr which decrements the use count. If the count becomes zero, the destructor from shared_ptr will delete the resources it point to.

当类型为 Query 的对象被复制、移动、赋值和销毁时会发生什么?

复制:在复制过程中,会调用合成的复制构造函数。它将数据成员复制到新对象中。由于在这种情况下,数据成员是一个共享指针,因此在复制时,对应的共享指针将指向相同的地址,并且两个共享指针的引用计数都变为 2。

移动:在移动过程中,会调用合成的移动构造函数。它将数据成员移动到新对象中。在这种情况下,新创建的对象中的共享指针将指向原始共享指针指向的地址。移动操作完成后,新对象中的共享指针的引用计数为 1,而原始对象中的指针变为 nullptr。

复制赋值:将调用合成的复制赋值操作符。这个操作的结果与复制操作相同。

移动赋值:将调用合成的移动赋值操作符。剩下的操作与移动操作相同。

销毁:将调用合成的析构函数。它将调用 shared_ptr 的析构函数,该析构函数会将引用计数减一。如果计数变为零,则 shared_ptr 的析构函数将删除其指向的资源。