-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path220329_move_in_class_hierarchy.cpp
More file actions
93 lines (80 loc) · 3.43 KB
/
220329_move_in_class_hierarchy.cpp
File metadata and controls
93 lines (80 loc) · 3.43 KB
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
// Based on Nicolai Josuttis' "C++ Move Semantics"
// Chapter on "Move Semantics in Class Hierarchies"
// IMPORTANT!!!
// Read code from the top!
// There are questions written in the comments.
// Think yourself of the answer before reading the code further down.
#include <cstdio>
#include <string>
#include <vector>
#include <array>
class GeoObj {
public:
GeoObj(std::string _name) : name(std::move(_name)) {}
// Q1. We want GeoObj to be abstract. Given that we already have draw as a pure virtual function,
// what could be a disadvantage of declaring the destructor as a pure virtual function?
virtual ~GeoObj() = 0;
virtual void draw() = 0;
protected:
// Q2. Why should we declare the copy/move constructors as protected?
GeoObj(const GeoObj& rhs) = default;
GeoObj(GeoObj&& rhs) noexcept = default;
// Q3. Why should we delete the copy/move assignment operators?
GeoObj& operator=(const GeoObj& rhs) = delete;
GeoObj& operator=(GeoObj&& rhs) = delete;
std::string name;
};
// A1. If we declare a pure virtual destructor, we have to provide a definition for it.
// If we are going to default it as below, why don't we just define it in the class as "virtual ~GeoObj() = default"?
GeoObj::~GeoObj() = default;
// Note: Check this out(copied from https://www.geeksforgeeks.org/pure-virtual-destructor-c/)
// > Why a pure virtual function requires a function body?
// > The reason is that destructors (unlike other functions) are not actually ‘overridden’,
// > rather they are always called in the reverse order of the class derivation. This means
// > that a derived class’ destructor will be invoked first, then base class destructor will
// > be called. If the definition of the pure virtual destructor is not provided, then what
// > function body will be called during object destruction? Therefore the compiler and linker
// > enforce the existence of a function body for pure virtual destructors.
// A2. To prevent implicit type conversion to GeoObj from its derived class objects. This applies only if the base class
// is not abstract. If the base class is not abstract and the copy/move constructors are public, then the following
// codes do compile, which is an implicit conversion from Polygon to GeoObj:
//
// GeoObj g{p}; // p is a Polygon
// GeoObj g{std::move(p)};
//
// Note that if the base class is abstract, above lines do not compile anyway because we cannot declare an abstract type
// GeoObj g directly
// A3. To prevent accidental slicing.
class Polygon : public GeoObj {
using Coor = std::vector<std::array<int, 2>>;
public:
Polygon(std::string name, Coor _coor) : GeoObj(std::move(name)), coor(std::move(_coor)) {}
// Q4. What happens if we uncomment the following line?
//~Polygon() = default;
void draw() {
printf("'%s'", name.c_str());
for (const auto& c : coor)
printf(" {%i, %i}", c[0], c[1]);
printf("\n");
}
protected:
Coor coor;
};
int main()
{
Polygon p0("p0", {{0, 0}, {3, 3}, {0, 6}});
Polygon p1 = p0;
Polygon p2 = std::move(p0);
p0.draw();
p1.draw();
p2.draw();
}
// A4. Declaring a destructor disables move semantics. The output will be changed.
// Before: (p0 moved to p2)
// ''
// 'p0' {0, 0} {3, 3} {0, 6}
// 'p0' {0, 0} {3, 3} {0, 6}
// After: (p0 copied to p2, move fallbacks to copy)
// 'p0' {0, 0} {3, 3} {0, 6}
// 'p0' {0, 0} {3, 3} {0, 6}
// 'p0' {0, 0} {3, 3} {0, 6}