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!