Verweise zu Lernmaterial

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
}