Skip to content

Commit a913458

Browse files
committed
feat: add concept ex for smart pointers
1 parent 6bcfce7 commit a913458

14 files changed

+18531
-0
lines changed

config.json

+12
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,18 @@
196196
"references",
197197
"headers"
198198
]
199+
},
200+
{
201+
"slug": "power-of-troy",
202+
"name": "Power of Troy",
203+
"uuid": "9f5e64e5-2838-4237-9cc6-fef500aacd7b",
204+
"concepts": [
205+
"auto",
206+
"smart-pointers"
207+
],
208+
"prerequisites": [
209+
"pointers"
210+
]
199211
}
200212
],
201213
"practice": [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Hints
2+
3+
## 1. Bring humans to the world of Troy
4+
5+
- The core of this exercise is the usage of `unique_ptr` and `shared_prt`.
6+
Which kind of smart pointer should be used for each of the `human` variables?
7+
- `artifacts` are not shared, but `powers` are needed to track influenced people.
8+
- the `possession` pointer should be unique, the power pointers should be shared.
9+
10+
## 2. Bring Artifacts into Troy
11+
12+
- You need to create a smart pointer and assign it to the `possession` variable
13+
- Use _references_ to change the object outside of the function.
14+
- Look up [`std::make_unique`][make_unique] for some hints.
15+
- The type of the pointer's target object has to be put in the angled brackets and again inside the parens to define the object.
16+
- Here is a full example: `variable = std::make_unique<int>(int{23});`
17+
18+
## 3. Make items tradeable
19+
20+
- You can look through the [unique_ptr reference][unique_ptr] to find a fitting function.
21+
- Do you think `std::swap` can help you?
22+
23+
## 4. Give Power to the People
24+
25+
- You need to create a smart pointer and assign it to the `own_power` variable
26+
- Use _references_ to change the object outside of the function.
27+
- Look up [`std::make_shared`][make_shared] for some hints.
28+
- The type of the pointer's target object has to be put in the angled brackets and again inside the parens to define the object.
29+
- Here is a full example: `variable = std::make_shared<int>(int{23});`
30+
31+
## 5. Use the Power
32+
33+
- Use _references_ to change the object outside of the function.
34+
35+
## 6. Keep watch on the power's intensity
36+
37+
- You can look through the [shared_ptr reference][shared_ptr] to find a fitting function.
38+
- Do you think `use_count` can help you?
39+
40+
[unique_ptr]: https://en.cppreference.com/w/cpp/memory/unique_ptr
41+
[make_unique]: https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique
42+
[shared_ptr]: https://en.cppreference.com/w/cpp/memory/shared_ptr
43+
[make_shared]: https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Instructions
2+
3+
This exercise takes you to the world of Troy.
4+
The lives of its people are full of wonder and magic.
5+
Many humans in Troy possess _powers_, that are used frequently in their daily lives.
6+
Powers are used to re-shape the world or influence Troy's fauna and other people.
7+
Magic also manifests in _unique artifacts_, that are highly sought after by adventurers, artisans and sages.
8+
9+
In this exercise you are going to write code to model the humans of Troy, their possessed artifacts and power interactions.
10+
11+
You have six tasks.
12+
The first one is related to creating a human, the other five are about handling powers and artifacts.
13+
14+
## 1. Bring humans to the world of Troy
15+
16+
For your model of Troy humans are the most important feature.
17+
Your model human should be able to possess a _unique artifact_.
18+
They should also have the ability to manifest a _power_.
19+
These powers might affect other humans, so you also want to model if a human is influenced by some other power.
20+
21+
You are provided with basic implementations of `artifact` and `power` structs.
22+
Implement a `human` struct (or class) that has a _smart-pointer_ to an `artifact` as `possession` member variable.
23+
Each artifact can only be possessed by a single human at any given time.
24+
25+
The `human` should also have variables for their `own_power` and `influenced_by`, which should be _smart-pointers_ to `powers`.
26+
Each `power` might be owned by a single human, but also influence other humans at the same time.
27+
28+
By default, humans are born without any artifact and neither own any powers nor are they influenced by them.
29+
30+
```cpp
31+
human mindy_mccready{};
32+
mindy_mccready.possession;
33+
// => nullptr
34+
mindy_mccready.own_power;
35+
// => nullptr
36+
mindy_mccready.influenced_by;
37+
// => nullptr
38+
```
39+
40+
## 2. Bring Artifacts into Troy
41+
42+
Your model is boring without the interaction of its parts.
43+
You want to create unique artifacts and give them to certain humans.
44+
45+
Define the function `give_new_artifact` which returns nothing but takes a `human` and a `string`.
46+
With the `string` it should define a new `artifact` object and set the `possession` pointer of the `human` accordingly.
47+
The function should not return anything.
48+
49+
```cpp
50+
human erik_magnus_lehnsherr{};
51+
give_new_artifact(erik_magnus_lehnsherr, "Mind shielding helmet");
52+
53+
erik_magnus_lehnsherr.possession->name;
54+
// "Mind shielding helmet"
55+
```
56+
57+
## 3. Make items tradeable
58+
59+
The world of Troy is all about interaction.
60+
You want people to make trades by exchanging their possessions.
61+
62+
Write a function `exchange_artifacts` that returns nothing but takes two artifact smart-pointers to exchange the items.
63+
64+
```cpp
65+
human uchiha{};
66+
give_new_artifact(uchiha, "konoha headband");
67+
human uzumaki{};
68+
give_new_artifact(uzumaki, "forehead protector");
69+
70+
exchange_artifacts(uchiha.possession, uzumaki.possession);
71+
72+
uchiha.possession->name;
73+
// "forehead protector"
74+
uzumaki.possession->name;
75+
// "konoha headband"
76+
```
77+
78+
## 4. Give Power to the People
79+
80+
The most exiting feature of Troy are the special powers, that people might wield.
81+
Some can smelt iron with their thoughts, while others can heal every wound instantly at nighttime.
82+
83+
Define the function `manifest_power` which returns nothing but takes a `human` and a `string`.
84+
With the `string` it should define a new `power` object and set the `own_power` pointer of the `human` accordingly.
85+
The function should not return anything.
86+
87+
```cpp
88+
human eleven {};
89+
manifest_power(eleven, "psychokinesis");
90+
91+
eleven.own_power->effect;
92+
// "psychokinesis"
93+
```
94+
95+
## 5. Use the Power
96+
97+
What use are the greatest powers, if you cannot use them.
98+
Your model concentrates on humans, so you want to track the influence of powers.
99+
100+
Write a _void_ function `use_power` that takes two humans, first: a caster and secondly: a target.
101+
The target's `influenced_by` pointer should be pointed to the power of the caster.
102+
103+
For simplicity, humans can only be influenced by a single power and this power stays in place even if the caster does not exist any longer.
104+
105+
```cpp
106+
human pamela_isley{};
107+
manifest_power(pamela_isley, "control pheromones");
108+
109+
human count_vertigo{};
110+
use_power(pamela_isley, count_vertigo);
111+
count_vertigo.influenced_by->effect;
112+
// "control pheromones"
113+
```
114+
115+
## 6. Keep watch on the power's intensity
116+
117+
Certain powers lose their potency or trigger certain effects in your simulation when they are applied to several humans.
118+
You want to track the number of people who are connected to each power.
119+
120+
Define the function `power_intensity`, that takes a human and returns the intensity of their power as an _int_.
121+
If the person has no power, the return value should be `0`.
122+
Otherwise the intensity should reflect the caster and all currently influenced people.
123+
124+
```cpp
125+
human jean_grey{};
126+
manifest_power(jean_grey, "uplifting personality");
127+
128+
human scott{};
129+
human logan{};
130+
human ororo{};
131+
132+
use_power(jean_grey, ororo);
133+
use_power(jean_grey, logan);
134+
use_power(jean_grey, scott);
135+
136+
power_intensity(jean_grey);
137+
// 4
138+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Introduction
2+
3+
Smart pointers are a modern C++ feature designed to provide automatic memory management, helping to prevent memory leaks and dangling pointers commonly associated with raw pointers.
4+
They act as wrappers around raw pointers, adding additional functionality such as automatic memory deallocation when the pointer is no longer needed.
5+
6+
## General Syntax
7+
8+
Smart pointers are typically implemented as class templates in the C++ standard library.
9+
The two most commonly used smart pointers are `std::unique_ptr` and `std::shared_ptr`.
10+
11+
## Unique Pointers
12+
13+
14+
`std::unique_ptr` is a smart pointer that owns the object exclusively.
15+
It ensures that at any given time, only one `std::unique_ptr` object owns the resource.
16+
When the owning `std::unique_ptr` is destroyed or reset, it automatically destructs the objects and releases its memory.
17+
18+
```cpp
19+
#include <memory>
20+
// Declaring and defining a unique pointer
21+
auto rightful_king_of_england = std::make_unique<std::string>("Excalibur");
22+
23+
// Unique pointers cannot be copied or assigned
24+
auto mordred = rightful_king_of_england; // Error: Cannot copy a unique_ptr
25+
```
26+
27+
## Advantages of `std::make_unique()`
28+
29+
When creating a `std::unique_ptr`, it's preferable to use `std::make_unique()` instead of directly using `new` to allocate memory.
30+
`std::make_unique()` provides several advantages:
31+
1. **Exception Safety**: `std::make_unique()` guarantees exception safety.
32+
If an exception is thrown during the construction of the object, memory will be automatically deallocated, preventing memory leaks.
33+
2. **Clarity**: Using `std::make_unique()` makes code clearer and more concise.
34+
It eliminates the need to explicitly specify the type being allocated, as the template arguments are deduced automatically.
35+
3. **Optimization Opportunities**: Compilers have the opportunity to optimize `std::make_unique()` more effectively than manually allocating memory with `new`, potentially resulting in improved performance.
36+
4. **Avoiding Misuse**: Deleting the underlying resource is possible, when the `std::unique_ptr` is constructed manually.
37+
That would lead to undefined behavior, when the `std::unique_ptr` tries to delete it at its end of scope.
38+
39+
## Shared Pointers
40+
41+
`std::shared_ptr` is a smart pointer that allows multiple `std::shared_ptr` objects to share ownership of the same resource.
42+
It keeps track of how many shared pointers are referencing the resource, and deallocates the memory only when the last shared pointer owning the resource goes out of scope or is reset.
43+
44+
```cpp
45+
// Declaring and defining a shared pointer to a dynamically allocated string
46+
auto martian_congressional_republic = std::make_shared<std::string>("protomolecule");
47+
48+
// Creating more shared pointer that shares ownership
49+
auto outer_planets_alliance = martian_congressional_republic;
50+
auto united_nations = martian_congressional_republic;
51+
```
52+
53+
~~~~exercism/caution
54+
In C++17 and below, using `std::shared_ptr` with arrays via `std::make_shared<T[]>` is not directly supported.
55+
While it's possible to allocate arrays with `std::make_shared<T[]>`, creating shared pointers directly from them may lead to undefined behavior due to differences in memory management between single objects and arrays.
56+
Instead, consider using `std::vector` or custom deletion functions to manage arrays with shared pointers effectively.
57+
Always ensure compatibility with your compiler and standard library implementation when dealing with array allocations and shared pointers in C++17.
58+
~~~~
59+
60+
## Advantages of `std::make_shared()`
61+
62+
Similar to `std::make_unique()`, `std::make_shared()` offers benefits such as improved memory efficiency, exception safety, and readability.
63+
It combines memory allocation for the control block and the managed object into a single operation, enhancing efficiency and reducing the risk of memory leaks.
64+
Additionally, automatic deduction of template arguments simplifies code and enhances readability.
65+
Using `std::make_shared()` promotes cleaner, safer, and more efficient code when working with `std::shared_ptr` objects in C++.
66+
67+
68+
~~~~exercism/advanced
69+
## Weak Pointers
70+
71+
`std::weak_ptr` is a companion class to `std::shared_ptr` that provides a non-owning "weak" reference to an object managed by a shared pointer.
72+
73+
```cpp
74+
// Creating a shared pointer
75+
auto your_account = std::make_shared<std::string>("secret_subscription_password");
76+
// Creating a shared pointer that shares ownership
77+
auto your_flatmates_account = your_account;
78+
79+
// Creating a weak pointer from the shared pointer
80+
auto your_flatmates_boyfriends_account = your_flatmates_account;
81+
// if your_account and your_flatmates_account are deleted, there is no more reference to the shared pointer.
82+
// your_flatmates_boyfriends_account will be a null pointer and cannot use the associated object any longer.
83+
```
84+
85+
Weak pointers are useful in scenarios where cyclic references need to be broken to prevent memory leaks.
86+
`std::weak_ptr` was designed to address the issue of cyclic ownership, also known as circular references, that can occur when using `std::shared_ptr`.
87+
In a cyclic ownership scenario, two or more `std::shared_ptr` objects are referencing each other, creating a cycle where none of the objects can be deleted because they have strong references to each other, leading to memory leaks.
88+
`std::weak_ptr` provides a solution to this problem by allowing weak references to shared objects without contributing to their reference count.
89+
This means that it can observe and access the shared object but doesn't prevent it from being deleted.
90+
~~~~
91+
92+
## Usage advice
93+
94+
Use smart pointers by default: `std::unique_ptr` for exclusive ownership and `std::shared_ptr` for shared ownership.
95+
Reserve raw pointers for non-owning references or when interfacing with legacy code.
96+
In most cases, `std::unique_ptr` is sufficient for exclusive ownership, as it offers lightweight memory management without the overhead of reference counting.
97+
`std::shared_ptr` should be used sparingly, as it introduces overhead and complexity unless true shared ownership is needed.
98+
`std::weak_ptr` is specialized for breaking cyclic dependencies or observing shared objects, but it's not commonly used.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"vaeng"
4+
],
5+
"files": {
6+
"solution": [
7+
"power_of_troy.cpp",
8+
"power_of_troy.h"
9+
],
10+
"test": [
11+
"power_of_troy_test.cpp"
12+
],
13+
"exemplar": [
14+
".meta/exemplar.cpp",
15+
".meta/exemplar.h"
16+
]
17+
},
18+
"blurb": "Learn about smart pointers by using magic and artifacts in the world of Troy."
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Design
2+
3+
## Learning objectives
4+
5+
- Know what a unique_ptr is
6+
- Know what a shared_ptr is
7+
8+
## Out of scope
9+
10+
- weak_ptr
11+
12+
## Concepts
13+
14+
- `smart-pointers`
15+
16+
## Prerequisites
17+
18+
- `pointers`
19+
20+
## Analyzer
21+
22+
- -
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include "power_of_troy.h"
2+
3+
namespace troy {
4+
5+
void give_new_artifact(human& receiver, std::string item_name) {
6+
receiver.possession =
7+
std::make_unique<artifact>(artifact{item_name}); // Include hint!
8+
}
9+
10+
void exchange_artifacts(std::unique_ptr<artifact>& item_a, std::unique_ptr<artifact>& item_b) {
11+
std::swap(item_a, item_b);
12+
}
13+
14+
void manifest_power(human& receiver, std::string power_effect) {
15+
receiver.own_power = std::make_shared<power>(power{power_effect});
16+
}
17+
18+
void use_power(const human& caster, human& receiver) {
19+
receiver.influenced_by = caster.own_power;
20+
}
21+
22+
int power_intensity(const human& caster) {
23+
return caster.own_power.use_count();
24+
}
25+
26+
} // namespace troy

0 commit comments

Comments
 (0)