Verweise zu Lernmaterial
- (Breymann 2023)
- 4.14 Alternative zu rohen Zeigern,
newunddelete - 8.2 Eine Klasse für Vektoren
- 21.3 Exception-sichere Beschaffung von Ressourcen
- 21.3.3
shared_ptrfür C-Arrays korrekt verwenden
- 4.14 Alternative zu rohen Zeigern,
- cppreference.com
- learncpp.com
- hackingcpp.com
Nutzen von Shared Pointer
Bei Shared Pointer handelt es sich um eine Variante des Smart-Pointer-Konzeptes. Im Gegensatz zur std::unique_ptr-Variante, bei der eine Ressource nur jeweils von genau einem std::unique_ptr verwaltet (besessen) werden kann, kann ein und dieselbe Ressource von mehr als einer std::unique_ptr-Instanz zur selben Zeit besessen werden. Die verschiedenen (für dieselbe Ressource verantwortlichen) Instanzen werden durch Kopieren erzeugt.
Intern führen die std::unique_ptr-Instanzen Protokoll darüber, wie viele std::unique_ptr-Instanzen dieselbe Ressource verwalten. Wird die letzte dieser std::unique_ptr-Instanzen zerstört, wird (im std::unique_ptr-Destruktor) auch die durch sie verwaltete Ressource freigegeben.
Die Standardbibliothek bietet mit std::shared_ptr eine Implementierung des Shared-Pointer-Konzeptes. Weiter weiter unten (Implementierung) in diesem Kapitel wird eine Implementierung gezeigt.
Beispiele
Beispiel 1
struct MyData {
int value;
MyData(int v) : value(v) { std::cout << "MyData(" << value << ") constructed\n"; }
~MyData() { std::cout << "MyData(" << value << ") destroyed\n"; }
};
int main() {
std::shared_ptr<MyData> p1(new MyData(42));
{
std::shared_ptr<MyData> p2 = p1;
std::cout << "Use count: " << p1.use_count() << "\n"; // 2
}
std::cout << "Use count after p2 is out of scope: " << p1.use_count() << "\n"; // 1
}Beispiel 2
#include <memory>
#include <iostream>
int main() {
std::shared_ptr<int> p1 = std::make_shared<int>(10);
std::shared_ptr<int> p2 = p1;
std::cout << "Use count: " << p1.use_count() << "\n"; // prints 2
p2.reset(); // decreases ref count
std::cout << "Use count after reset: " << p1.use_count() << "\n"; // prints 1
}Beispiel 3
const auto size = ...;
std::shared_ptr<Heap_Memory> s = std::make_shared<Heap_Memory>(size);
std::thread t1( [s]() { /* use s */ } );
std::thread t3( [s]() { /* use s */ } );
/* use s */Implementierung
Es folgt eine Implementierung von Shared Pointer, die der std::unique_ptr-Implementierung aus der Standardbibliothek sehr nahe kommt. Der Quellcode ist in diesem Repository zu finden: https://github.fh-zwickau.de/whz-modul-pti06830-cpp/mysharedptr.
#include <cstddef>// for size_t
#include <utility>// for std::exchange
template<typename T> class SharedPtr
{
private:
struct ControlBlock
{
T *ptr;
size_t ref_count{};
explicit ControlBlock(T *p) : ptr{ p }, ref_count{ 1 } {}
};
ControlBlock *control;
void release()
{
if (control) {
if (--control->ref_count == 0) {
delete control->ptr;
delete control;
}
control = nullptr;
}
}
public:
// Default constructor
SharedPtr() : control(nullptr) {}
// Constructor from raw pointer
explicit SharedPtr(T *ptr) : control(new ControlBlock(ptr)) {}
// Copy constructor
SharedPtr(const SharedPtr &other) : control(other.control)
{
if (control) { ++control->ref_count; }
}
// Move constructor
SharedPtr(SharedPtr &&other) noexcept : control(std::exchange(other.control, nullptr)) {}
// Copy assignment
SharedPtr &operator=(const SharedPtr &other)
{
if (this != &other) {
release();
control = other.control;
if (control) { ++control->ref_count; }
}
return *this;
}
// Move assignment
SharedPtr &operator=(SharedPtr &&other) noexcept
{
if (this != &other) {
release();
control = std::exchange(other.control, nullptr);
}
return *this;
}
// Destructor
~SharedPtr() { release(); }
// Dereference operators
T &operator*() const { return *(control->ptr); }
T *operator->() const { return control->ptr; }
// Get raw pointer
[[nodiscard]] T *get() const { return control ? control->ptr : nullptr; }
// Get use count
[[nodiscard]] size_t use_count() const { return control ? control->ref_count : 0; }
// Check if non-empty
explicit operator bool() const { return control && control->ptr; }
};Funktionsweise von SharedPtr anhand eines Beispiels
struct MyData {
int value;
MyData(int v) : value(v) { std::cout << "MyData(" << value << ") constructed\n"; }
~MyData() { std::cout << "MyData(" << value << ") destroyed\n"; }
};
int main() {
SharedPtr<MyData> p1(new MyData(42));
{
SharedPtr<MyData> p2 = p1;
std::cout << "Use count: " << p1.use_count() << "\n"; // 2
{
SharedPtr<MyData> p3 = p2;
std::cout << "Use count: " << p1.use_count() << "\n"; // 3
}
}
std::cout << "Use count after p2 is out of scope: " << p1.use_count() << "\n"; // 1
}