Conheça o ponteiro determinista do coletor de lixo

Memória, em C ++ sempre foi difícil trabalhar com ela (um legado amargo de C ) ... Aqui o C ++ 11 com seu std :: shared_ptr vem em nosso auxílio.


Como você deve ter adivinhado, se essas primitivas não tivessem problemas, esse artigo não teria sido :)

Vejamos o exemplo a seguir de um vazamento de memória clássico em std :: shared_ptr :

#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; } 

Obviamente, não veremos o chamado de destruidores de objetos. Como lidar com isso? std :: weak_ptr vem em nosso auxílio:

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

Sim, ajuda a resolver o problema. Mas se você tem uma hierarquia mais complexa de objetos e é muito difícil entender quem deve fazer std :: weak_ptr e quem deve ser std :: shared_ptr ? Ou você não quer mexer com conexões soltas?

Coletor de lixo é o nosso tudo !!

Não, claro que não. No C ++, não há suporte nativo para o Garbage Collector e, mesmo que seja adicionado, obtemos custos indiretos para o Garbage Collector, além de quebras de RAII.

O que fazemos?

O Ponteiro determinístico do coletor de lixo é um ponteiro que rastreia todos os links dos objetos raiz e, assim que nenhum dos objetos raiz se refere ao nosso objeto, ele é excluído imediatamente.

O princípio de sua operação é semelhante ao std :: shared_ptr (rastreia o escopo ), mas também aos objetos que fazem referência a ele.

Vejamos o princípio de sua operação no exemplo anterior:

 #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; } 

Como você pode ver, o código se tornou um pouco mais, mas esse é o preço que você precisa pagar pela remoção totalmente automática de objetos. Pode-se observar que métodos adicionais connectToRoot econnectFromRoot foram adicionados. É claro que escrever com as mãos o tempo todo será bastante difícil, então pretendo criar um pequeno gerador desses métodos em classes que usam gc_ptr (como vemos, aderimos ao princípio Zero-Overhead , não pagamos pelo que não usamos e, se usarmos- então os custos não são mais do que se o escrevêssemos com as mãos).

A biblioteca gc_ptr.hpp é segura para threads, não cria nenhum thread adicional para coleta de lixo, tudo é feito nos operadores construtor, destruidor e de atribuição, portanto, se sobrescrevermos nosso objeto e nenhum objeto raiz se referir mais a ele, retornaremos memória alocada para o nosso objeto.

Obrigado pela atenção!

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


All Articles