Lernen Sie den deterministischen Garbage Collector-Zeiger kennen

Speicher, in C ++ war es immer schwierig, damit zu arbeiten (ein bitteres Erbe von C ) ... Hier hilft uns C ++ 11 mit seinem std :: shared_ptr .


Wie Sie vielleicht erraten haben, wäre dieser Artikel nicht gewesen, wenn diese Grundelemente keine Probleme gehabt hätten :)

Schauen wir uns das folgende Beispiel eines klassischen Speicherverlusts auf std :: shared_ptr an :

#include <iostream> #include <memory> class Child; class Parent { public: Parent() { std::cout << "Parent()" << std::endl; } ~Parent() { std::cout << "~Parent()" << std::endl; } void createChild() { child_ptr_ = std::make_shared<Child>(); } std::shared_ptr<Child> getChild() { return child_ptr_; } private: std::shared_ptr<Child> child_ptr_; }; class Child { public: Child() { std::cout << "Child()" << std::endl; } ~Child() { std::cout << "~Child()" << std::endl; } void setParent(std::shared_ptr<Parent> parentPtr) { parent_ptr_ = parentPtr; } private: std::shared_ptr<Parent> parent_ptr_; }; int main() { auto parent = std::make_shared<Parent>(); parent->createChild(); parent->getChild()->setParent(parent); return 0; } 

Offensichtlich werden wir den Aufruf von Objektzerstörern nicht sehen. Wie gehe ich damit um? std :: schwach_ptr hilft uns:

 ... class Child { ... void setParent(std::shared_ptr<Parent> parentPtr) { parent_ptr_ = parentPtr; } private: std::weak_ptr<Parent> parent_ptr_; }; ... 

Ja, es hilft, das Problem zu lösen. Aber wenn Sie eine komplexere Hierarchie von Objekten haben und es sehr schwer zu verstehen ist, wer std :: schwach_ptr und wer std :: shared_ptr sein sollte ? Oder wollen Sie überhaupt nicht mit losen Verbindungen herumspielen?

Müllsammler ist unser Alles !!

Nein, natürlich nicht. In C ++ gibt es keine native Unterstützung für den Garbage Collector, und selbst wenn wir ihn hinzufügen, erhalten wir Gemeinkosten für den Garbage Collector sowie RAII-Unterbrechungen.

Was machen wir

Der deterministische Garbage Collector-Zeiger ist ein Zeiger, der alle Verknüpfungen von Stammobjekten verfolgt. Sobald sich keines der Stammobjekte auf unser Objekt bezieht, wird er sofort gelöscht.

Das Funktionsprinzip ähnelt std :: shared_ptr (es verfolgt den Bereich ), aber auch Objekte, die darauf verweisen.

Schauen wir uns das Funktionsprinzip im vorherigen Beispiel an:

 #include <iostream> #include "gc_ptr.hpp" class Child; class Parent { public: Parent() { std::cout << "Parent()" << std::endl; } ~Parent() { std::cout << "~Parent()" << std::endl; } void createChild() { child_ptr_.create_object(); } memory::gc_ptr<Child> getChild() { return child_ptr_; } void connectToRoot(void * rootPtr) { child_ptr_.connectToRoot(rootPtr); } void disconnectFromRoot(bool isRoot, void * rootPtr) { child_ptr_.disconnectFromRoot(isRoot, rootPtr); } private: memory::gc_ptr<Child> child_ptr_; }; class Child { public: Child() { std::cout << "Child()" << std::endl; } ~Child() { std::cout << "~Child()" << std::endl; } void setParent(memory::gc_ptr<Parent> parentPtr) { parent_ptr_ = parentPtr; } void connectToRoot(void * rootPtr) { parent_ptr_.connectToRoot(rootPtr); } void disconnectFromRoot(bool isRoot, void * rootPtr) { parent_ptr_.disconnectFromRoot(isRoot, rootPtr); } private: memory::gc_ptr<Parent> parent_ptr_; }; int main() { memory::gc_ptr<Parent> parent; parent.create_object(); parent->createChild(); parent->getChild()->setParent(parent); return 0; } 

Wie Sie sehen können, ist der Code etwas größer geworden, aber dies ist der Preis, den Sie für das vollautomatische Entfernen von Objekten zahlen müssen. Es ist ersichtlich, dass zusätzliche Methoden connectToRoot undconnectFromRoot hinzugefügt wurden. Natürlich wird es ziemlich schwierig sein, die ganze Zeit mit den Händen zu schreiben. Ich beabsichtige daher, einen kleinen Generator dieser Methoden in Klassen zu erstellen , die gc_ptr verwenden (da wir diese sehen, halten wir uns an das Zero-Overhead- Prinzip, zahlen wir nicht für das, was wir nicht verwenden, und wenn wir verwenden) dann sind die Kosten nicht höher als wenn wir es mit unseren Händen geschrieben haben).

Die Bibliothek gc_ptr.hpp ist threadsicher, sie erstellt keine zusätzlichen Threads für die Garbage Collection. Alles wird im Konstruktor-, Destruktor- und Zuweisungsoperator ausgeführt. Wenn wir also unser Objekt überschreiben und kein Stammobjekt mehr darauf verweist, kehren wir zurück Speicher für unser Objekt zugewiesen.

Vielen Dank für Ihre Aufmerksamkeit!

Source: https://habr.com/ru/post/de462583/


All Articles