-
Notifications
You must be signed in to change notification settings - Fork 68
/
problem3.cpp
125 lines (89 loc) · 3.58 KB
/
problem3.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
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
#include <iostream>
#include <array>
#include <vector>
#include <random>
#include <algorithm>
/* --------------------------------------------------------------------------------------------
* Shared ownership.
*
* Most of the time, ownership can be solved by having one owner (with `std::unique_ptr`) and
* one or more observers (raw pointers or references). Sometimes, we need to truly share data,
* though. Here is an example of a completely messed up ownership model, which could be
* fixed using shared_ptr.
*
* Tasks
* 1) Verify the mess by repeatedly running it using such a command like:
* `while true; do ./problem3 ; done`
* You should notice that the program regularly leaks.
* 2) Fix the ownership model using `std::shared_ptr` !
* - Convert the vectors to holding `std::shared_ptr`.
* - Fix the arguments of the functions.
* 3) Speed optimisation: make sure that you don't create & destroy useless `std::shared_ptr`,
* which is slow, for example in the for loop of `doStuff()` and when
* calling `changeLargeObject()`.
* --------------------------------------------------------------------------------------------
*/
// The class LargeObject emulates a large object.
// One should avoid to copy it, and rather use
// a pointer to pass it around.
struct LargeObject {
std::array<double, 100000> data ;
// So to check for some potential memory leak,
// we count the constructions and destructions
inline static std::size_t count = 0;
LargeObject() { count++ ; }
~LargeObject() { count-- ; }
} ;
// This removes an element from a non-owning vector,
// in a random place. Such elements can by known in
// several vectors, so they must not be deleted.
void removeRandom( std::vector<LargeObject *> & collection, std::default_random_engine & engine ) {
// MAKE YOUR CHANGES IN THIS FUNCTION
auto pos = collection.begin() + engine() % collection.size() ;
collection.erase(pos);
}
// A function to do something with a large object.
// Note that since we don't own the object,
// we don't need a smart pointer as argument.
void changeLargeObject( LargeObject & object ) {
object.data[0] = 1. ;
}
// Global stuff: we have pointers to objects duplicated in two different collections.
// We work a bit with the collections, and then we try to clean up without neither
// memory leak nor segmentation fault. Without a shared ownership model, this becomes a mess.
void doStuff() {
// MAKE YOUR CHANGES IN THIS FUNCTION
// Prepare a non deterministic random engine
std::random_device device ;
std::default_random_engine engine(device()) ;
// Original collection
std::vector<LargeObject*> objVector(10);
for ( auto & ptr : objVector ) {
ptr = new LargeObject();
}
// Let's copy the whole collection
auto objVectorCopy(objVector);
// Random work with the objects
removeRandom(objVector,engine);
removeRandom(objVectorCopy,engine);
removeRandom(objVectorCopy,engine);
// ...
for (auto objPtr : objVector ) {
changeLargeObject(*objPtr) ;
}
// ONCE YOU FIXED CODE ABOVE WITH SHARED POINTERS
// THE UGLY CODE BELOW SHOULD BECOME UNNECESSARY
for ( auto objPtr : objVector ) {
delete objPtr ;
}
for ( auto objPtr : objVectorCopy ) {
// If the element is in the original collection, it was already deleted.
if (std::find(objVector.begin(), objVector.end(), objPtr) == objVector.end()) {
delete objPtr;
}
}
}
int main() {
doStuff() ;
std::cout<<"Leaked large objects: "<<LargeObject::count<<std::endl ;
}